[
  {
    "path": ".github/actions/setup/action.yml",
    "content": "name: 'Setup Node.js and dependencies'\ndescription: 'Sets up Node.js, caches dependencies, and installs packages'\nruns:\n  using: \"composite\"\n  steps:\n    - uses: actions/checkout@v4\n    - name: Use Node.js LTS\n      uses: actions/setup-node@v4\n      with:\n        node-version: 'lts/*'\n    - name: Cache dependencies\n      uses: actions/cache@v4\n      with:\n        path: ~/.cache\n        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}\n        restore-keys: |\n          ${{ runner.os }}-yarn-\n    - run: yarn install\n      shell: bash\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: master\n  pull_request:\n    branches: master\n\njobs:\n  cypress-tests:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n      - name: Run Cypress tests\n        run: yarn cypress:test\n\n  unit-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n      - run: yarn test:ci\n\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n      - run: yarn lint\n\n  audit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n      - run: yarn audit\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n.temp\n.cache\n\n*.swo\n*.swp\n*.log\n\nnode_modules\n\ndist\n\ncypress/videos\n\ncoverage\n\n.history\n"
  },
  {
    "path": ".husky/.gitignore",
    "content": "_\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn test\n"
  },
  {
    "path": ".npmrc",
    "content": "message=\"version v%s\"\n"
  },
  {
    "path": ".run/Main Jest.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Main Jest\" type=\"JavaScriptTestRunnerJest\">\n    <config-file value=\"$PROJECT_DIR$/jest.config.js\" />\n    <node-interpreter value=\"project\" />\n    <node-options value=\"\" />\n    <jest-package value=\"$PROJECT_DIR$/node_modules/jest\" />\n    <working-dir value=\"$PROJECT_DIR$\" />\n    <jest-options value=\"--watch\" />\n    <envs>\n      <env name=\"NODE_ENV\" value=\"test\" />\n    </envs>\n    <scope-kind value=\"ALL\" />\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "\n{\n  \"recommendations\": [\n    \"coenraads.bracket-pair-colorizer\",\n    \"dbaeumer.vscode-eslint\",\n    \"eamodio.gitlens\",\n    \"christian-kohler.npm-intellisense\",\n    \"vscode-icons-team.vscode-icons\",\n    \"jpoissonnier.vscode-styled-components\",\n    \"ofhumanbondage.react-proptypes-intellisense\",\n    \"christian-kohler.path-intellisense\",\n    \"saharavr.react-component-splitter\",\n    \"mhutchie.git-graph\",\n    \"dsznajder.es7-react-js-snippets\",\n    \"github.vscode-github-actions\"\n  ]\n}"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"name\": \"vscode-jest-tests\",\n      \"request\": \"launch\",\n      \"program\": \"${workspaceFolder}/node_modules/jest/bin/jest\",\n      \"args\": [\n        \"--runInBand\",\n      ],\n      \"cwd\": \"${workspaceFolder}\",\n      \"console\": \"integratedTerminal\",\n      \"internalConsoleOptions\": \"neverOpen\",\n      \"disableOptimisticBPs\": true\n    },\n    {\n      \"type\": \"node\",\n      \"name\": \"vscode-jest-tests-no-cache\",\n      \"request\": \"launch\",\n      \"program\": \"${workspaceFolder}/node_modules/jest/bin/jest\",\n      \"args\": [\n        \"--runInBand\",\n        \"--watch\",\n        \"--no-cache\"\n      ],\n      \"cwd\": \"${workspaceFolder}\",\n      \"console\": \"integratedTerminal\",\n      \"internalConsoleOptions\": \"neverOpen\",\n      \"disableOptimisticBPs\": true\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.snippetSuggestions\": \"top\",\n  \"editor.trimAutoWhitespace\": true,\n  \"editor.tabSize\": 2,\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll\": \"explicit\"\n  },\n\n  \"eslint.validate\": [\"javascript\", \"javascriptreact\", \"typescript\", \"typescriptreact\"],\n  \"eslint.alwaysShowStatus\": true,\n  \"eslint.format.enable\": true,\n  \"eslint.codeActionsOnSave.mode\": \"problems\",\n  \"editor.formatOnSave\": true,\n\n  \"flow.enabled\": false,\n\n  \"typescript.tsdk\": \"./node_modules/typescript/lib\",\n  \"typescript.locale\": \"en\",\n  \"typescript.preferences.quoteStyle\": \"single\",\n\n  \"jestrunner.debugOptions\": {\"args\": [\"--watch\"]},\n  \"jestrunner.configPath\": \"jest.config.js\",\n\n  \"cSpell.words\": [\n    \"astring\",\n    \"lcov\",\n    \"nollup\",\n    \"postversion\",\n    \"Vitali\",\n    \"WDYR\",\n    \"welldone\",\n    \"Zaidman\"\n  ],\n}"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018-present, Vitali Zaidman <vzaidman@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"images/WDYR-logo.jpg\" width=\"300px\" />\n</p>\n\n# Why Did You Render\n\n[![npm version](https://badge.fury.io/js/%40welldone-software%2Fwhy-did-you-render.svg)](https://badge.fury.io/js/%40welldone-software%2Fwhy-did-you-render)\n[![Build Status](https://github.com/welldone-software/why-did-you-render/actions/workflows/main.yml/badge.svg)](https://github.com/welldone-software/why-did-you-render/actions/workflows/main.yml)\n[![license](https://img.shields.io/npm/l/@welldone-software/why-did-you-render?style=flat)](https://github.com/welldone-software/why-did-you-render/blob/master/LICENSE)\n[![@welldone-software/why-did-you-render](https://snyk.io/advisor/npm-package/@welldone-software/why-did-you-render/badge.svg)](https://snyk.io/advisor/npm-package/@welldone-software/why-did-you-render)\n[![Coverage Status](https://coveralls.io/repos/github/welldone-software/why-did-you-render/badge.svg?branch=add-e2e-tests-using-cypress)](https://coveralls.io/github/welldone-software/why-did-you-render?branch=add-e2e-tests-using-cypress)\n\n`why-did-you-render` by [Welldone Software](https://welldone.software/) monkey patches **`React`** to notify you about potentially avoidable re-renders. (Works with **`React Native`** as well.)\n\nFor example, if you pass `style={{width: '100%'}}` to a big memo component it would always re-render on every element creation:\n```jsx\n<MemoBigList style={{width: '100%'}}/>\n```\nIt can also help you to simply track when and why a certain component re-renders.\n\n> [!CAUTION]\n> The library was not tested with [React Compiler](https://react.dev/learn/react-compiler) at all. I believe it's completely incompatible with it.\n\n> [!CAUTION]\n> Not all re-renders are *\"bad\"*. Sometimes shenanigan to reduce re-renders can either hurt your App's performance or have a negligible effect, in which case it would be just a waste of your efforts, and complicate your code. Try to focus on heavier components when optimizing and use the [React DevTools Profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to measure the effects of any changes.\n\n> [!NOTE]\nI've joined the React team, specifically working on React tooling. This role has opened up exciting opportunities to enhance the developer experience for React users— and your input could offer valuable insights to help me with this effort. Please join the conversation in the [discussion thread](https://github.com/welldone-software/why-did-you-render/discussions/309)!\n\n## Setup\nThe latest version of the library was tested [(unit tests and E2E)]((https://travis-ci.com/welldone-software/why-did-you-render.svg?branch=master)) with **`React@19`** only.\n* [For `React 18`, please see the readme for version @^8](https://github.com/welldone-software/why-did-you-render/tree/version-8).\n* [For `React 17` and `React 16`, please see the readme for version @^7](https://github.com/welldone-software/why-did-you-render/tree/version-7).\n\n```\nnpm install @welldone-software/why-did-you-render --save-dev\n```\nor\n```\nyarn add @welldone-software/why-did-you-render -D\n```\nSet the library to be the React's importSource and make sure `preset-react` is in `development` mode.\n\nThis is because `React 19` requires using the `automatic` [JSX transformation](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). \n```js\n['@babel/preset-react', {\n  runtime: 'automatic',\n  development: process.env.NODE_ENV === 'development',\n  importSource: '@welldone-software/why-did-you-render',\n}]\n```\n\n### React Native\n\n#### Bare workflow\n\nAdd the plugin as listed below and start react-native packager as usual. Default env for babel is \"development\". If you do not use expo when working with react-native, the following method will help you.\n\n```js\nmodule.exports = {\n  presets: ['module:metro-react-native-babel-preset'],\n\n  env: {\n    development: {\n      plugins: [['@babel/plugin-transform-react-jsx', {\n        runtime: 'automatic',\n        development: process.env.NODE_ENV === 'development',\n        importSource: '@welldone-software/why-did-you-render',\n      }]],\n    },\n  },\n}\n```\n\n#### Expo managed\n\nYou can pass params to `@babel/preset-react` through `babel-preset-expo`\n\n```js\n// babel.config.js\nmodule.exports = function (api) {\n  api.cache(true);\n  return {\n    presets: [\n      [\n        \"babel-preset-expo\",\n        {\n          jsxImportSource: \"@welldone-software/why-did-you-render\",\n        },\n      ],\n    ],\n  };\n};\n```\n\n> Notice: Create React App (CRA) ^4 **uses the `automatic` JSX transformation.**\n> [See the following comment on how to do this step with CRA](https://github.com/welldone-software/why-did-you-render/issues/154#issuecomment-773905769)\n\nCreate a `wdyr.js` file and import it as **the very first import** in your application.\n\n`wdyr.js`:\n```jsx\nimport React from 'react';\n\nif (process.env.NODE_ENV === 'development') {\n  const whyDidYouRender = require('@welldone-software/why-did-you-render');\n  whyDidYouRender(React, {\n    trackAllPureComponents: true,\n  });\n}\n```\n\n> [!CAUTION]\n> The library should *NEVER* be used in production because:\n> - It significantly slows down React\n> - It monkey patches React and can result in unexpected behavior\n\nIn [Typescript](https://github.com/welldone-software/why-did-you-render/issues/161), call the file wdyr.ts and add the following line to the top of the file to import the package's types:\n```tsx\n/// <reference types=\"@welldone-software/why-did-you-render\" />\n```\n\nImport `wdyr` as the first import (even before `react-hot-loader` if you use it):\n\n`index.js`:\n\n```jsx\nimport './wdyr'; // <--- first import\n\nimport 'react-hot-loader';\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n// ...\nimport {App} from './app';\n// ...\nReactDOM.render(<App/>, document.getElementById('root'));\n```\n\nIf you use `trackAllPureComponents`, all pure components ([React.PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) or [React.memo](https://reactjs.org/docs/react-api.html#reactmemo)) will be tracked.\n\nOtherwise, add `whyDidYouRender = true` to ad-hoc components to track them. (f.e `Component.whyDidYouRender = true`)\n\nMore information about what is tracked can be found in [Tracking Components](#tracking-components).\n\nCan't see any WDYR logs? Check out the [troubleshooting section](#troubleshooting) or search in the [issues](https://github.com/welldone-software/why-did-you-render/issues).\n\n## Custom Hooks\n\nAlso, tracking custom hooks is possible by using `trackExtraHooks`. For example if you want to track `useSelector` from React Redux:\n\n`wdyr.js`:\n```jsx\nimport React from 'react';\n\n// For react-native you might want to use \n// the __DEV__ flag instead of process.env.NODE_ENV === 'development'\nif (process.env.NODE_ENV === 'development') {\n  const whyDidYouRender = require('@welldone-software/why-did-you-render');\n  const ReactRedux = require('react-redux');\n  whyDidYouRender(React, {\n    trackAllPureComponents: true,\n    trackExtraHooks: [\n      [ReactRedux, 'useSelector']\n    ]\n  });\n}\n```\n\n> Notice that there's currently a problem with rewriting exports of imported files in `webpack`. A quick workaround can help with it: [#85 - trackExtraHooks cannot set property](https://github.com/welldone-software/why-did-you-render/issues/85).\n\n## Read More\n* [Why Did You Render Mr. Big Pure React Component???](http://bit.ly/wdyr1)\n* [**Common fixing scenarios** this library can help with](http://bit.ly/wdyr02)\n* [**React Hooks** - Understand and fix hooks issues](http://bit.ly/wdyr3)\n* [Why Did You Render v4 Released!](https://medium.com/welldone-software/why-did-you-render-v4-released-48e0f0b99d4c) - TypeScript support, Custom hooks tracking (like React-Redux’s useSelector), Tracking of all pure components.\n\n## Integration With Other Libraries\n* [Next.js example](https://github.com/zeit/next.js/tree/canary/examples/with-why-did-you-render)\n* [React-Redux With Hooks](https://medium.com/welldone-software/why-did-you-render-v4-released-48e0f0b99d4c)\n* [Mobx is currently not supported](https://github.com/welldone-software/why-did-you-render/issues/162)\n* [React-Native flipper plugin made by @allen-hsu](https://github.com/allen-hsu/wdyr-flipper#wdry-flipper-reporter)\n\n## Sandbox\nYou can test the library in [the official sandbox](http://bit.ly/wdyr-sb).\n\nAnd another [official sandbox with hooks tracking](https://codesandbox.io/s/why-did-you-render-sandbox-with-hooks-pyi14)\n\n## Tracking Components\nYou can track all pure components ([React.PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) or [React.memo](https://reactjs.org/docs/react-api.html#reactmemo)) using the `trackAllPureComponents: true` option.\n\nYou can also manually track any component you want by setting `whyDidYouRender` on them like this:\n```js\nclass BigList extends React.Component {\n  static whyDidYouRender = true\n  render(){\n    return (\n      //some heavy render you want to ensure doesn't happen if its not necessary\n    )\n  }\n}\n```\n\nOr for functional components:\n\n```js\nconst BigListPureComponent = props => (\n  <div>\n    //some heavy component you want to ensure doesn't happen if its not necessary\n  </div>\n)\nBigListPureComponent.whyDidYouRender = true\n```\n\nYou can also pass an object to specify more advanced tracking settings:\n\n```js\nEnhancedMenu.whyDidYouRender = {\n  logOnDifferentValues: true,\n  customName: 'Menu'\n}\n```\n\n- `logOnDifferentValues`:\n\n  Normally, only re-renders that are caused by equal values in props / state trigger notifications:\n  ```js\n  render(<Menu a={1}/>)\n  render(<Menu a={1}/>)\n  ```\n  This option will trigger notifications even if they occurred because of different props / state (Thus, because of \"legit\" re-renders):\n  ```js\n  render(<Menu a={1}/>)\n  render(<Menu a={2}/>)\n  ```\n\n- `customName`:\n\n  Sometimes the name of the component can be missing or very inconvenient. For example:\n\n  ```js\n  withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamespace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu)))))))))))))))))))))))\n  ```\n\n## Options\nOptionally you can pass in `options` as the second parameter. The following options are available:\n- `include: [RegExp, ...]` (`null` by default)\n- `exclude: [RegExp, ...]` (`null` by default)\n- `trackAllPureComponents: false`\n- `trackHooks: true`\n- `trackExtraHooks: []`\n- `logOwnerReasons: true`\n- `logOnDifferentValues: false`\n- `hotReloadBufferMs: 500`\n- `onlyLogs: false`\n- `collapseGroups: false`\n- `titleColor`\n- `diffNameColor`\n- `diffPathColor`\n- `textBackgroundColor`\n- `notifier: ({Component, displayName, hookName, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason, options, ownerDataMap}) => void`\n- `getAdditionalOwnerData: (element) => {...}`\n\n#### include / exclude\n##### (default: `null`)\n\nYou can include or exclude tracking of components by their displayName using the `include` and `exclude` options.\n\nFor example, the following code is used to [track all redundant re-renders that are caused by older React-Redux](http://bit.ly/wdyr04):\n```js\nwhyDidYouRender(React, { include: [/^ConnectFunction/] });\n```\n> *Notice: **exclude** takes priority over both `include` and manually set `whyDidYouRender = `*\n\n#### trackAllPureComponents\n##### (default: `false`)\n\nYou can track all pure components (both `React.memo` and `React.PureComponent` components)\n\n> *Notice: You can exclude the tracking of any specific component with `whyDidYouRender = false`*\n\n#### trackHooks\n##### (default: `true`)\n\nYou can turn off tracking of hooks changes.\n\n[Understand and fix hook issues](http://bit.ly/wdyr3).\n\n#### trackExtraHooks\n##### (default: `[]`)\n\nTrack custom hooks:\n\n```js\nwhyDidYouRender(React, {\n  trackExtraHooks: [\n    // notice that 'useSelector' is a named export\n    [ReactRedux, 'useSelector'],\n  ]\n});\n```\n\n> This feature is rewriting exports of imported files. There is currently a problem with that approach in webpack. A workaround is available here: [#85 - trackExtraHooks cannot set property](https://github.com/welldone-software/why-did-you-render/issues/85)\n\n#### logOwnerReasons\n##### (default: `true`)\n\nOne way of fixing re-render issues is preventing the component's owner from re-rendering.\n\nThis option is `true` by default and it lets you view the reasons why an owner component re-renders.\n\n![demo](images/logOwnerReasons.png)\n\n#### logOnDifferentValues\n##### (default: `false`)\n\nNormally, you only want logs about component re-renders when they could have been avoided.\n\nWith this option, it is possible to track all re-renders.\n\nFor example:\n```js\nrender(<BigListPureComponent a={1}/>)\nrender(<BigListPureComponent a={2}/>)\n// will only log if you use {logOnDifferentValues: true}\n```\n\n#### hotReloadBufferMs\n##### (default: `500`)\n\nTime in milliseconds to ignore updates after a hot reload is detected.\n\nWhen a hot reload is detected, we ignore all updates for `hotReloadBufferMs` to not spam the console.\n\n#### onlyLogs\n##### (default: `false`)\n\nIf you don't want to use `console.group` to group logs you can print them as simple logs.\n\n#### collapseGroups\n##### (default: `false`)\n\nGrouped logs can be collapsed.\n\n#### titleColor / diffNameColor / diffPathColor / textBackgroundColor\n##### (default titleColor: `'#058'`)\n##### (default diffNameColor: `'blue'`)\n##### (default diffPathColor: `'red'`)\n##### (default textBackgroundColor: `'white`)\n\nControls the colors used in the console notifications\n\n#### notifier\n##### (default: defaultNotifier that is exposed from the library)\n\nYou can create a custom notifier if the default one does not suite your needs.\n\n#### getAdditionalOwnerData\n##### (default: `undefined`)\nYou can provide a function that harvests additional data from the original react element. The object returned from this function will be added to the ownerDataMap which can be accessed later within your notifier function override.\n\n## Troubleshooting\n\n### No tracking\n* If you are in production, WDYR is probably disabled.\n* Maybe no component is tracked\n    * Check out [Tracking Components](#tracking-components) once again.\n* If you only track pure components using `trackAllPureComponents: true` then you would only track either ([React.PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) or [React.memo](https://reactjs.org/docs/react-api.html#reactmemo)), maybe none of your components are pure so none of them will get tracked.\n* Maybe you have no issues\n    * Try causing an issue by temporary rendering the whole app twice in it's entry point:\n\n        `index.js`:\n        ```jsx\n        const HotApp = hot(App);\n        HotApp.whyDidYouRender = true;\n        ReactDOM.render(<HotApp/>, document.getElementById('root'));\n        ReactDOM.render(<HotApp/>, document.getElementById('root'));\n        ```\n\n### Custom Hooks tracking (like useSelector)\nThere's currently a problem with rewriting exports of imported files in `webpack`. A quick workaround can help with it: [#85 - trackExtraHooks cannot set property](https://github.com/welldone-software/why-did-you-render/issues/85).\n\n### React-Redux `connect` HOC is spamming the console\nSince `connect` hoists statics, if you add WDYR to the inner component, it is also added to the HOC component where complex hooks are running.\n\nTo fix this, add the `whyDidYouRender = true` static to a component after the connect:\n```js\n  const SimpleComponent = ({a}) => <div data-testid=\"foo\">{a.b}</div>)\n  // not before the connect:\n  // SimpleComponent.whyDidYouRender = true\n  const ConnectedSimpleComponent = connect(\n    state => ({a: state.a})\n  )(SimpleComponent)\n  // after the connect:\n  SimpleComponent.whyDidYouRender = true\n```\n\n### Sourcemaps\nTo see the library's sourcemaps use the [source-map-loader](https://webpack.js.org/loaders/source-map-loader/).\n\n## Credit\n\nInspired by the following previous work:\n\n* github.com/maicki/why-did-you-update (no longer public) which I had the chance to maintain for some time.\n* https://github.com/garbles/why-did-you-update where [A deep dive into React perf debugging](https://benchling.engineering/a-deep-dive-into-react-perf-debugging-fd2063f5a667/) is credited for the idea.\n\n## License\n\nThis library is [MIT licensed](./LICENSE).\n\n[🔼Back to top!](#Why-Did-You-Render)\n"
  },
  {
    "path": "babel.config.cjs",
    "content": "const compact = require('lodash/compact');\n\nmodule.exports = function(api) {\n  const isProd = process.env.NODE_ENV === 'production';\n  const isTest = process.env.NODE_ENV === 'test';\n\n  api.cache(false);\n\n  const presets = [\n    ['@babel/preset-env', {\n      modules: isTest ? 'commonjs' : false,\n    }],\n    ['@babel/preset-react', {\n      runtime: 'automatic',\n      development: true,\n      importSource: `${__dirname}`,\n    }],\n  ];\n\n  const plugins = compact([\n    (!isProd && !isTest) && 'react-refresh/babel',\n  ]);\n\n  return {presets, plugins};\n};\n"
  },
  {
    "path": "cypress/.eslintrc",
    "content": "{\n  \"extends\": [\n    \"plugin:cypress/recommended\"\n  ]\n}\n"
  },
  {
    "path": "cypress/babel.config.js",
    "content": "module.exports = require('../babel.config.cjs').default;\n"
  },
  {
    "path": "cypress/e2e/big_list.js",
    "content": "it('Big list basic example', () => {\n  cy.visitAndSpyConsole('/#bigList',console => {\n    cy.contains('button', 'Increase!').click();\n\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'BigList', times: 1},\n      {match: /props.*style\\W/, times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because of props changes'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/child-of-pure-component.js",
    "content": "it('Child of Pure Component', () => {\n  cy.visitAndSpyConsole('/#childOfPureComponent', console => {\n    cy.contains('button', 'clicks:').click();\n    cy.contains('button', 'clicks:').click();\n  \n    cy.contains('button', 'clicks:').should('contain', '2');\n\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'PureFather', times: 2},\n      {match: /props.*children\\W/, times: 2},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: 'syntax always produces a *NEW* immutable React element', times: 2},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/clone-element.js",
    "content": "it('Creating react element using React.cloneElement', () => {\n  cy.visitAndSpyConsole('/#cloneElement', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'TestComponent', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/create-factory.js",
    "content": "it('Creating react element using React.createFactory', () => {\n  cy.visitAndSpyConsole('/#createFactory', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'TestComponent', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/hooks-use-context.js",
    "content": "it('Hooks - useContext', () => {\n  cy.visitAndSpyConsole('/#useContext', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: /ComponentWithContextHook$/, times: 2},\n      {match: 'Rendered by Main', times: 1},\n      {match: 'ComponentWithContextHookInsideMemoizedParent', times: 1},\n      {match: '[hook useState result]', times: 1},\n      {match: '[hook useContext result]', times: 2},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},\n      {match: [() => true, 'Re-rendered because of hook changes'], times: 3},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/hooks-use-memo-and-callback-child.js",
    "content": "it('Hooks - useMemo and useCallback Child', () => {\n  cy.visitAndSpyConsole('/#useMemoAndCallbackChild', console => {\n    cy.contains('button', 'count: 0').click();\n\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'Comp', times: 2},\n      {match: /useMemoFn/, times: 2},\n      {match: /useCallbackFn/, times: 2},\n      {match: /props.*\\..*count/, times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/hooks-use-reducer.js",
    "content": "it('Hooks - useReducer', () => {\n  const checkConsole = (console, times) => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'Main', times},\n      {match: '[hook useReducer result]', times},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: 'different objects that are equal by value.', times},\n    ]);\n  };\n\n  cy.visitAndSpyConsole('/#useReducer', console => {\n    cy.contains('button', 'broken set count').click();\n\n    checkConsole(console, 1);\n  \n    cy.contains('button', 'broken set count').click();\n  \n    checkConsole(console, 2);\n  \n    cy.contains('button', 'correct set count').click();\n  \n    checkConsole(console, 2); // should not cause a re-render because of a current useRender user\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/hooks-use-state.js",
    "content": "it('Hooks - useState', () => {\n  cy.visitAndSpyConsole('/#useState', console => {\n    cy.get('button:contains(\"Re-render\")')\n      .should('have.length', 4)\n      .each($btn => {\n        cy.wrap($btn).click();\n      });\n\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'BrokenHooksPureComponent', times: 2},\n      {match: '[hook useState result]', times: 2},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/hot-reload.js",
    "content": "it('React Hot Reload Of Tracked Component', () => {\n  cy.visitAndSpyConsole('/#hotReload', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'HotExportedDemoComponent', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/no-change.js",
    "content": "it('No Changes', () => {\n  cy.visitAndSpyConsole('/#noChanges', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered although props and state objects are the same.'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/owner-reasons.js",
    "content": "it('Log Owner Reasons', () => {\n  cy.visitAndSpyConsole('/#logOwnerReasons', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'Child', times: 3},\n      {match: 'Rendered by Owner', times: 1},\n      {match: 'Rendered by ClassOwner', times: 1},\n      {match: 'Rendered by HooksOwner', times: 1},\n      {match: /props.*a\\W/, times: 1},\n      {match: '[hook useState result]', times: 2},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal'], times: 3},\n      {match: [() => true, 'Re-rendered because of props changes'], times: 1},\n      {match: [() => true, 'Re-rendered because of state changes'], times: 1},\n      {match: [() => true, 'Re-rendered because of hook changes'], times: 2},\n      {match: 'different objects.', times: 4},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/props-and-state-change.js",
    "content": "it('Props And State Changes', () => {\n  cy.visitAndSpyConsole('/#bothChanges', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 1},\n      {match: /props.*a\\W/, times: 1},\n      {match: /state.*c\\W/, times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: 'different objects that are equal by value.', times: 2},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/props-changes.js",
    "content": "it('props changes', () => {\n  cy.visitAndSpyConsole('/#propsChanges', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 5},\n      {match: 'Rendered by Main', times: 5},\n      {match: /props.*a\\W/, times: 4},\n      {match: /props.*containerProps\\W/, times: 4},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/react-redux.js",
    "content": "describe('react-redux', () => {\n  it('React Redux', () => {\n    const checkConsole = (console, times) => {\n      expect(console.group).to.be.calledWithMatches([\n        {match: 'ConnectedSimpleComponent', times},\n        {match: '[hook useSelector result]', times},\n      ]);\n\n      expect(console.log).to.be.calledWithMatches([\n        {match: [() => true, 'Re-rendered because of hook changes'], times},\n      ]);\n    };\n\n    cy.visitAndSpyConsole('/#reactRedux', console => {\n      cy.contains('button', 'Same State').click();\n\n      checkConsole(console, 0);\n  \n      cy.contains('button', 'Deep Equal State').click();\n  \n      checkConsole(console, 1);\n  \n      cy.contains('button', 'Deep Equal State').click();\n  \n      checkConsole(console, 2);\n  \n      cy.contains('button', 'Random Object').click();\n  \n      checkConsole(console, 2); // should not cause a re-render because the random object is different from the older one\n    });\n  });\n\n  it('React Redux HOC', () => {\n    const checkConsole = (console, times) => {\n      expect(console.group).to.be.calledWithMatches([\n        {match: 'SimpleComponent', times: times * 2},\n        {match: /props.*a\\W/, times},\n      ]);\n\n      expect(console.log).to.be.calledWithMatches([\n        {match: [() => true, 'Re-rendered because of props changes'], times},\n        {match: 'different objects that are equal by value', times},\n      ]);\n    };\n\n    cy.visitAndSpyConsole('/#reactReduxHOC', console => {\n      cy.contains('button', 'Same State').click();\n\n      checkConsole(console, 0);\n  \n      cy.contains('button', 'Deep Equal State').click();\n  \n      checkConsole(console, 1);\n  \n      cy.contains('button', 'Deep Equal State').click();\n  \n      checkConsole(console, 2);\n  \n      cy.contains('button', 'Random Object').click();\n  \n      checkConsole(console, 2); // should not cause a re-render because the random object is different from the older one\n    });\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/special-changes.js",
    "content": "it('Special Changes', () => {\n  cy.visitAndSpyConsole('/#specialChanges', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: 'different regular expressions with the same value.', times: 1},\n      {match: 'different functions with the same name.', times: 1},\n      {match: 'different date objects with the same value.', times: 1},\n      {match: 'different React elements (remember that the <jsx/> syntax always produces a *NEW* immutable React element', times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/ssr.js",
    "content": "it('Server Side (hydrate)', () => {\n  cy.visitAndSpyConsole('/#ssr', console => {\n    cy.contains('hydrated hi');\n\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'DemoComponent', times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/state-changes.js",
    "content": "it('state changes', () => {\n  cy.visitAndSpyConsole('/#stateChanges', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 2},\n      {match: /state.*objectKey\\W/, times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the state object itself changed but its values are all equal'], times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/strict-mode.js",
    "content": "it('Strict mode', () => {\n  cy.visitAndSpyConsole('/#strict', console => {\n    expect(console.group).to.be.calledWithMatches([\n      {match: 'ClassDemo', times: 3},\n      {match: 'Rendered by Main', times: 3},\n      {match: /props.*a\\W/, times: 4},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 2},\n      {match: 'different objects that are equal by value', times: 4},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/styled-component.js",
    "content": "it('styled-components', () => {\n  cy.visitAndSpyConsole('/#styledComponents', console => {\n    cy.get('div:contains(\"styled-components\")')\n      .last()\n      .should('have.css', 'background-color', 'rgb(255, 150, 174)');\n  \n    expect(console.group).to.be.calledWithMatches([\n      {match: 'Styled(SimpleComponent)', times: 1},\n      {match: /props.*a\\W/, times: 1},\n    ]);\n\n    expect(console.log).to.be.calledWithMatches([\n      {match: [() => true, 'Re-rendered because of props changes'], times: 1},\n      {match: 'different objects that are equal by value', times: 1},\n    ]);\n  });\n});\n"
  },
  {
    "path": "cypress/e2e/test_console_assertions.js",
    "content": "it('Test console testing throws on wrong console appearance amounts', () => {\n  cy.visitAndSpyConsole('/#bigList', console => {\n    cy.contains('button', 'Increase!').click();\n\n    expect(\n      () => expect(console.group).to.be.calledWithMatches([\n        {match: 'BigList', times: 0},\n      ])\n    ).to.throw();\n\n    expect(\n      () => expect(console.log).to.be.calledWithMatches([\n        {match: [() => true, 'Re-rendered because of props changes'], times: 0},\n      ])\n    ).to.throw();\n  });\n});\n"
  },
  {
    "path": "cypress/fixtures/example.json",
    "content": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mock data for responses to routes\"\n}\n"
  },
  {
    "path": "cypress/plugins/index.js",
    "content": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins/index.js can be used to load plugins\n//\n// You can change the location of this file or turn off loading\n// the plugins file with the 'pluginsFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/plugins-guide\n// ***********************************************************\n\n// This function is called when a project is opened or re-opened (e.g. due to\n// the project's config changing)\n\n/**\n * @type {Cypress.PluginConfig}\n */\nmodule.exports = (on, config) => { // eslint-disable-line no-unused-vars\n  // `on` is used to hook into various events Cypress emits\n  // `config` is the resolved Cypress config\n};\n"
  },
  {
    "path": "cypress/support/assertions.js",
    "content": "chai.util.addChainableMethod(chai.Assertion.prototype, 'calledWithMatches', function(matchConfigs) {\n  const calls = this._obj.getCalls();\n\n  matchConfigs.forEach(matchConfig => {\n    if (!matchConfig.match) {\n      throw new Error('Every item in calledWithMatches should have a match prop');\n    }\n\n    const matchedCalls = calls.filter(call => {\n      return call.calledWithMatch(...Cypress._.castArray(matchConfig.match));\n    });\n\n    if ('times' in matchConfig) {\n      chai.assert(\n        matchConfig.times === matchedCalls.length,\n        `${this._obj} was expected to be called with ${matchConfig.match} for ${matchConfig.times} times but got ${matchedCalls.length} times`\n      );\n    } else {\n      chai.assert(\n        matchedCalls.length > 0,\n        `${this._obj} was expected to be called with ${matchConfig.match}`\n      );\n    }\n  });\n});\n"
  },
  {
    "path": "cypress/support/commands.js",
    "content": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom commands and overwrite\n// existing commands.\n//\n// For more comprehensive examples of custom\n// commands please read more here:\n// https://on.cypress.io/custom-commands\n// ***********************************************\n//\n//\n// -- This is a parent command --\n// Cypress.Commands.add(\"login\", (email, password) => { ... })\n//\n//\n// -- This is a child command --\n// Cypress.Commands.add(\"drag\", { prevSubject: 'element'}, (subject, options) => { ... })\n//\n//\n// -- This is a dual command --\n// Cypress.Commands.add(\"dismiss\", { prevSubject: 'optional'}, (subject, options) => { ... })\n//\n//\n// -- This will overwrite an existing command --\n// Cypress.Commands.overwrite(\"visit\", (originalFn, url, options) => { ... })\n\nCypress.Commands.add('visitAndSpyConsole', (url, cb) => {\n  const context = {};\n\n  cy.visit(url, {\n    onBeforeLoad: win => {\n      cy.spy(win.console, 'log');\n      cy.spy(win.console, 'group');\n    },\n    onLoad: win => context.win = win,\n  });\n\n  cy.waitFor(context.win)\n    .then(() => cb(context.win.console));\n});\n"
  },
  {
    "path": "cypress/support/e2e.js",
    "content": "// ***********************************************************\n// This example support/index.js is processed and\n// loaded automatically before your test files.\n//\n// This is a great place to put global configuration and\n// behavior that modifies Cypress.\n//\n// You can change the location of this file or turn off\n// automatically serving support files with the\n// 'supportFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/configuration\n// ***********************************************************\n\n// Import commands.js using ES2015 syntax:\nimport './commands';\nimport './assertions';\n\n// Alternatively you can use CommonJS syntax:\n// require('./commands')\n"
  },
  {
    "path": "cypress.config.ts",
    "content": "import { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  projectId: 'k4cvdh',\n  e2e: {\n    // We've imported your old cypress plugins here.\n    // You may want to clean this up later by importing these.\n    setupNodeEvents(on, config) {\n      return require('./cypress/plugins/index.js')(on, config)\n    },\n    baseUrl: 'http://localhost:3003',\n    specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',\n  },\n})\n"
  },
  {
    "path": "demo/nollup.config.js",
    "content": "const replace = require('@rollup/plugin-replace');\nconst babel = require('@rollup/plugin-babel').default;\nconst nodeResolve = require('rollup-plugin-node-resolve');\nconst alias = require('rollup-plugin-alias');\nconst commonjs = require('rollup-plugin-commonjs-alternate');\nconst refresh = require('rollup-plugin-react-refresh');\n\nmodule.exports = {\n  input: 'demo/src/index.js',\n  output: {\n    file: 'app._hash_.js',\n    format: 'esm',\n    assetFileNames: '[name][extname]',\n  },\n  plugins: [\n    alias({\n      entries: {\n        '@welldone-software/why-did-you-render': `${__dirname}/../src/index.js`,\n      },\n    }),\n    replace({\n      preventAssignment: true,\n      values: {\n        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n        'process.env.PORT': JSON.stringify(process.env.PORT),\n      }\n    }),\n    babel({\n      exclude: 'node_modules/**',\n      babelHelpers: 'bundled',\n    }),\n    nodeResolve({\n      mainFields: ['module', 'browser', 'main'],\n    }),\n    commonjs({}),\n    refresh(),\n  ],\n};\n"
  },
  {
    "path": "demo/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>whyDidYouRender demos</title>\n</head>\n<body>\n  <div id=\"menu\"></div>\n  <div id=\"demo\"></div>\n  <script src=\"app._hash_.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "demo/serve.js",
    "content": "const React = require('react');\nconst ReactDomServer = require('react-dom/server');\nconst express = require('express');\nconst fallback = require('express-history-api-fallback');\nconst http = require('http');\n\nconst config = require('./nollup.config.js');\nconst nollupDevServer = require('nollup/lib/dev-middleware');\nconst DemoComponent = require('./src/ssr/DemoComponent');\n\nconst port = process.env.PORT;\nif (!port) {\n  throw new Error('please specify PORT in env variables.');\n}\n\nconst app = express();\n\napp.get('/ssrComponent', (req, res) => {\n  const html = ReactDomServer.renderToString(\n    React.createElement(DemoComponent, {text: 'hydrated hi'})\n  );\n  res.send(html);\n});\n\nconst server = http.createServer(app);\n\napp.use(nollupDevServer(app, config, {\n  watch: ['demo/src', 'src'],\n  hot: true,\n}, server));\n\napp.use(express.static('demo/public'));\n\napp.use(fallback('index.html', {root: 'demo/public'}));\n\nserver.listen(port, () => {\n  // eslint-disable-next-line no-console\n  console.log(`Listening on http://localhost:${port}`);\n});\n"
  },
  {
    "path": "demo/src/App.js",
    "content": "import React from 'react';\nimport ReactDom from 'react-dom/client';\n\nimport whyDidYouRender from '@welldone-software/why-did-you-render';\n\nimport Menu from './Menu';\n\nimport bigList from './bigList';\nimport propsChanges from './propsChanges';\nimport stateChanges from './stateChanges';\nimport childOfPureComponent from './childOfPureComponent';\nimport bothChanges from './bothChanges';\nimport noChanges from './noChanges';\nimport specialChanges from './specialChanges';\nimport ssr from './ssr';\nimport hotReload from './hotReload';\nimport createFactory from './createFactory';\nimport cloneElement from './cloneElement';\nimport useState from './hooks/useState';\nimport useContext from './hooks/useContext';\nimport useMemoAndCallbackChild from './hooks/useMemoAndCallbackChild';\nimport useReducer from './hooks/useReducer';\nimport reactReduxHOC from './reactReduxHOC';\nimport strict from './strict';\nimport reactRedux from './reactRedux';\nimport styledComponents from './styledComponents';\nimport logOwnerReasons from './logOwnerReasons';\nimport forwardRef from './forwardRef';\n\nconst demosList = {\n  bigList,\n  propsChanges,\n  stateChanges,\n  childOfPureComponent,\n  bothChanges,\n  noChanges,\n  specialChanges,\n  ssr,\n  hotReload,\n  createFactory,\n  cloneElement,\n  useState,\n  useContext,\n  useMemoAndCallbackChild,\n  useReducer,\n  strict,\n  reactRedux,\n  reactReduxHOC,\n  styledComponents,\n  logOwnerReasons,\n  forwardRef,\n};\n\nconst defaultDemoName = 'bigList';\n\nconst domElement = document.getElementById('demo');\nlet reactDomRoot;\n\nfunction changeDemo(demoFn, {shouldCreateRoot = true} = {}) {\n  console.clear && console.clear(); // eslint-disable-line no-console\n  React.__REVERT_WHY_DID_YOU_RENDER__ && React.__REVERT_WHY_DID_YOU_RENDER__();\n  reactDomRoot?.unmount();\n  if (shouldCreateRoot) {\n    reactDomRoot = ReactDom.createRoot(domElement);\n  }\n  setTimeout(() => {\n    const reactDomRootPromise = demoFn({whyDidYouRender, domElement, reactDomRoot});\n    if (reactDomRootPromise) {\n      reactDomRootPromise.then(r => reactDomRoot = r);\n    }\n  }, 1);\n}\n\nconst demoFromHash = demosList[window.location.hash.substr(1)];\nconst initialDemo = demoFromHash || demosList[defaultDemoName];\nif (!demoFromHash) {\n  window.location.hash = defaultDemoName;\n}\n\nchangeDemo(initialDemo.fn, initialDemo.settings);\n\nconst DemoLink = ({name, description, fn, settings}) => (\n  <li><a href={`#${name}`} onClick={() => changeDemo(fn, settings)}>{description}</a></li>\n);\n\nconst App = () => (\n  <Menu>\n    {\n      Object\n        .entries(demosList)\n        .map(([demoName, demoData]) => <DemoLink key={demoName} name={demoName} {...demoData}/>)\n    }\n  </Menu>\n);\n\nexport default App;\n\n\n"
  },
  {
    "path": "demo/src/Menu.js",
    "content": "import React from 'react';\n\nlet Menu = ({children}) => (\n  <div>\n    <h1>whyDidYouRender Demos</h1>\n    <h3>\n      <span style={{backgroundColor: '#dad'}}>&nbsp;Open the console&nbsp;</span>\n      &nbsp;and click on one of the demos\n    </h3>\n    <ul>\n      {children}\n    </ul>\n  </div>\n);\n\nexport default Menu;\n"
  },
  {
    "path": "demo/src/bigList/index.js",
    "content": "import React from 'react';\nimport {times} from 'lodash';\n\nexport default {\n  description: 'Big List (Main Demo)',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    class BigListPureComponent extends React.PureComponent {\n      static whyDidYouRender = {customName: 'BigList'};\n      render() {\n        return (\n          <div style={this.props.style}>\n            <h2>BigListPureComponent</h2>\n            <div>\n              {times(3000).map(n => <div key={n}>Element #{n}</div>)}\n            </div>\n          </div>\n        );\n      }\n    }\n\n    const bigListStyle = {width: '100%'}; // eslint-disable-line no-unused-vars\n\n    // Notice, that unlike the huge list, we don't track Main's re-renders because we don't care about it's re-renders.\n    class Main extends React.Component {\n      state = {count: 0};\n      render() {\n        return (\n          <div style={{height: '100%', width: '100%', display: 'flex', flexDirection: 'column'}}>\n            <h1>Big List (Main Demo)</h1>\n            <p>\n              {'Open the console and notice how the heavy list re-renders on every click on \"Increase!\" even though it\\'s props are the same.'}\n            </p>\n            <div>\n              <button onClick={() => {this.setState({count: this.state.count + 1});}}>\n                Increase!\n              </button>\n            </div>\n            <div>\n              <span>Count: {this.state.count}</span>\n            </div>\n            {/* this is how you can prevent re-renders: */}\n            {/* <BigListPureComponent style={bigListStyle}/> */}\n            <BigListPureComponent style={{width: '100%'}}/>\n          </div>\n        );\n      }\n    }\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/bothChanges/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Props And State Changes',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    class ClassDemo extends React.Component {\n      static whyDidYouRender = true;\n\n      state = {\n        c: {d: 'd'},\n      };\n\n      static getDerivedStateFromProps() {\n        return {\n          c: {d: 'd'},\n        };\n      }\n\n      render() {\n        return <div>State And Props Changes</div>;\n      }\n    }\n\n    stepLogger('First Render');\n    reactDomRoot.render(<ClassDemo a={{b: 'b'}}/>);\n\n    stepLogger('Second Render', true);\n    reactDomRoot.render(<ClassDemo a={{b: 'b'}}/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/childOfPureComponent/index.js",
    "content": "import React from 'react';\n\nexport default {\n  description: 'Child of Pure Component',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React, {\n      trackAllPureComponents: true,\n    });\n\n    const SomeChild = () => (\n      <div>Child!</div>\n    );\n\n    class PureFather extends React.PureComponent {\n      render() {\n        return (\n          <div>\n            {this.props.children}\n          </div>\n        );\n      }\n    }\n\n    class Main extends React.Component {\n      state = {clicksCount: 0};\n      render() {\n        return (\n          <div>\n            <button onClick={() => this.setState({clicksCount: this.state.clicksCount + 1})}>\n              clicks: {this.state.clicksCount}\n            </button>\n            <PureFather>\n              <SomeChild/>\n            </PureFather>\n          </div>\n        );\n      }\n    }\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/cloneElement/index.js",
    "content": "import React from 'react';\n\nexport default {\n  description: 'Creating react element using React.cloneElement',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    class TestComponent extends React.Component {\n      static whyDidYouRender = true;\n      render() {\n        return (\n          <div>\n            TestComponent\n          </div>\n        );\n      }\n    }\n\n    const testElement = <TestComponent a={1}/>;\n    const testElement2 = React.cloneElement(testElement);\n\n    reactDomRoot.render(testElement);\n    reactDomRoot.render(testElement2);\n  },\n};\n"
  },
  {
    "path": "demo/src/createFactory/index.js",
    "content": "import React from 'react';\n\nexport default {\n  description: 'Creating react element using React.createFactory',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    class TestComponent extends React.Component {\n      static whyDidYouRender = true;\n      render() {\n        return (\n          <div>\n            TestComponent\n          </div>\n        );\n      }\n    }\n\n    const TestComponentFactory = React.createFactory(TestComponent);\n\n    reactDomRoot.render(TestComponentFactory({a: 1}));\n    reactDomRoot.render(TestComponentFactory({a: 1}));\n  },\n};\n"
  },
  {
    "path": "demo/src/createStepLogger.js",
    "content": "const shouldTriggerComment = 'Should trigger whyDidYouRender';\nconst shouldNotTriggerComment = 'Shouldn\\'t trigger whyDidYouRender';\n\nexport default function createStepLogger() {\n  let step = 0;\n  return function stepLogger(description = '', shouldTrigger) {\n    const comment = shouldTrigger ? shouldTriggerComment : shouldNotTriggerComment;\n    // eslint-disable-next-line no-console\n    console.log(\n      `\\nRender #${step++} %c${description} %c${comment}`,\n      'color:blue',\n      shouldTrigger ? 'color:red' : 'color:green'\n    );\n  };\n}\n"
  },
  {
    "path": "demo/src/forwardRef/index.js",
    "content": "import React from 'react';\n\nexport default {\n  description: 'forwardRef',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    const Main = React.forwardRef((props, ref) => {\n      return <div ref={ref}>hi</div>;\n    });\n\n    Main.whyDidYouRender = true;\n\n    Main.displayName = 'Main';\n\n    const App = () => {\n      const [,setState] = React.useState(0);\n      \n      React.useLayoutEffect(() => {\n        setState(s => s + 1);\n      }, []);\n\n      return <Main a={[]}/>;\n    };\n\n    App.displayName = 'App';\n\n    reactDomRoot.render(<App/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/hooks/useContext.js",
    "content": "import React from 'react';\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Hooks - useContext',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    const stepLogger = createStepLogger();\n\n    const MyContext = React.createContext({c: 'c'});\n\n    let alreadyMountedComponentWithContextHook = false;\n    function ComponentWithContextHook() {\n      if (alreadyMountedComponentWithContextHook) {\n        stepLogger('renders ComponentWithContextHook with deep equal context', true);\n      } else {\n        alreadyMountedComponentWithContextHook = true;\n      }\n\n      const currentContext = React.useContext(MyContext);\n\n      return (\n        <p>{currentContext.c}</p>\n      );\n    }\n    ComponentWithContextHook.whyDidYouRender = true;\n\n    let alreadyMountedComponentWithContextHookInsideMemoizedParent = false;\n    function ComponentWithContextHookInsideMemoizedParent() {\n      if (alreadyMountedComponentWithContextHookInsideMemoizedParent) {\n        stepLogger('renders ComponentWithContextHookInsideMemoizedParent with deep equal context', true);\n      } else {\n        alreadyMountedComponentWithContextHookInsideMemoizedParent = true;\n      }\n\n      const currentContext = React.useContext(MyContext);\n\n      return (\n        <p>{currentContext.c}</p>\n      );\n    }\n    ComponentWithContextHookInsideMemoizedParent.whyDidYouRender = true;\n\n    const MemoizedParent = React.memo(() => (\n      <div>\n        <ComponentWithContextHookInsideMemoizedParent/>\n      </div>\n    ));\n\n    MemoizedParent.displayName = 'MemoizedParent';\n    MemoizedParent.whyDidYouRender = true;\n\n    let alreadyMountedMain = false;\n    function Main() {\n      const [currentState, setCurrentState] = React.useState({c: 'context value'});\n\n      if (alreadyMountedMain) {\n        stepLogger('renders Main and it would trigger the render of ComponentWithContextHook because it\\'s not pure', true);\n      } else {\n        alreadyMountedMain = true;\n      }\n\n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'context value'});\n      }, []);\n\n      return (\n        <MyContext value={currentState}>\n          <h3>\n            {`While somehow weird, we have two notifications for \"ComponentWithContextHook\"\n            since it is re-rendered regardless of context changes because \"Main\" is\n            re-rendered and ComponentWithContextHook is not pure`}\n          </h3>\n          <div>\n            ComponentWithContextHook\n            <ComponentWithContextHook />\n            <br/>\n            <br/>\n            MemoizedParent\n            <MemoizedParent />\n          </div>\n        </MyContext>\n      );\n    }\n\n    stepLogger('initial render');\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/hooks/useMemoAndCallbackChild.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Hooks - useMemo and useCallback Child',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    const Comp = ({useMemoFn, useCallbackFn}) => {\n      const onClick = (...args) => {\n        useMemoFn(...args);\n        useCallbackFn(...args);\n      };\n      return <div onClick={onClick}>hi!</div>;\n    };\n    Comp.displayName = 'Comp';\n    Comp.whyDidYouRender = true;\n\n    const ComponentWithNewResultsForNewDeps = React.memo(({count}) => {\n      stepLogger('render component with always new results for new deps');\n\n      const useMemoFn = React.useMemo(() => () => 'a', [count]);\n      const useCallbackFn = React.useCallback(() => 'a', [count]);\n\n      return (\n        <Comp useMemoFn={useMemoFn} useCallbackFn={useCallbackFn}/>\n      );\n    });\n    ComponentWithNewResultsForNewDeps.displayName = 'ComponentWithNewResultsForNewDeps';\n\n    const ComponentWithNewResultsForDeepEqualsDeps = React.memo(({count}) => {\n      if (count === 0) {\n        stepLogger('render component with always deep equals results - first render', false);\n      } else {\n        stepLogger('render component with always deep equals results - next render', true);\n      }\n\n      const useMemoFn = React.useMemo(() => () => 'a', [{dep1: 'dep1'}]);\n      const useCallbackFn = React.useCallback(() => 'a', [{dep2: 'dep2'}]);\n\n      return (\n        <Comp useMemoFn={useMemoFn} useCallbackFn={useCallbackFn}/>\n      );\n    });\n    ComponentWithNewResultsForDeepEqualsDeps.displayName = 'ComponentWithNewResultsForDeepEqualsDeps';\n\n    function Main() {\n      const [count, setCount] = React.useState(0);\n\n      return (\n        <div>\n          <button onClick={() => setCount(count + 1)}>\n            Current count: {count}\n          </button>\n          <ComponentWithNewResultsForNewDeps count={count}/>\n          <ComponentWithNewResultsForDeepEqualsDeps count={count}/>\n        </div>\n      );\n    }\n\n    Main.displayName = 'Main';\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/hooks/useReducer.js",
    "content": "/* eslint-disable no-console */\nimport React from 'react';\n\nexport default {\n  description: 'Hooks - useReducer',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    function reducer(state, action) {\n      switch (action.type) {\n\n      case 'broken-set-count':\n        return {count: action.payload.count};\n\n      case 'set-count':\n        if (action.payload.count === state.count) {\n          return state;\n        }\n        return {count: action.payload.count};\n      }\n    }\n\n    const initialState = {count: '0'};\n\n    function Main() {\n      const [state, dispatch] = React.useReducer(reducer, initialState);\n      const inputRef = React.createRef();\n\n      return (\n        <div>\n          <p>current count: {state.count}</p>\n          <input ref={inputRef} defaultValue=\"0\"/>\n          <button\n            onClick={() => dispatch({\n              type: 'broken-set-count',\n              payload: {count: inputRef.current.value},\n            })}\n          >\n            broken set count\n          </button>\n          <button\n            onClick={() => dispatch({\n              type: 'set-count',\n              payload: {count: inputRef.current.value},\n            })}\n          >\n            correct set count\n          </button>\n          <br />\n          <button onClick={() => console.clear()}>clear console</button>\n        </div>\n      );\n    }\n    Main.whyDidYouRender = true;\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/hooks/useState.js",
    "content": "/* eslint-disable no-console */\nimport React from 'react';\n\nexport default {\n  description: 'Hooks - useState',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    function BrokenHooksComponent() {\n      console.log('render BrokenHooksComponent');\n      const [numObj, setNumObj] = React.useState({num: 0});\n      return (\n        <>\n          <p>{'Will cause a re-render since {num: 0} !== {num: 0}'}</p>\n          <button onClick={() => setNumObj({num: 0})}>\n            Will Cause a Re-render: {numObj.num}\n          </button>\n        </>\n      );\n    }\n    BrokenHooksComponent.whyDidYouRender = true;\n\n    const BrokenHooksPureComponent = React.memo(BrokenHooksComponent);\n    BrokenHooksPureComponent.displayName = 'BrokenHooksPureComponent';\n    BrokenHooksPureComponent.whyDidYouRender = true;\n\n    function CorrectHooksComponent() {\n      console.log('render CorrectHooksComponent');\n      const [num, setNum] = React.useState(0);\n      return (\n        <>\n          <p>{'Will NOT cause a re-render since 0 === 0'}</p>\n          <button onClick={() => setNum(0)}>\n            Will NOT Cause a Re-render: {num}\n          </button>\n        </>\n      );\n    }\n    CorrectHooksComponent.whyDidYouRender = true;\n\n    function useNumState(defState) {\n      const [state, setState] = React.useState(defState);\n\n      function smartSetState(newState) {\n        if (state.num !== newState.num) {\n          setState(newState);\n        }\n      }\n\n      return [state, smartSetState];\n    }\n\n    function SmartHooksComponent() {\n      console.log('render SmartHooksComponent');\n      const [numObj, setNumObj] = useNumState({num: 0});\n      return (\n        <>\n          <p>{'Will NOT cause a re-render setState won\\'t be called'}</p>\n          <button onClick={() => setNumObj({num: 0})}>\n            Will NOT Cause a Re-render: {numObj.num}\n          </button>\n        </>\n      );\n    }\n    SmartHooksComponent.whyDidYouRender = true;\n\n    function Main() {\n      return (\n        <div>\n          BrokenHooksPureComponent\n          <BrokenHooksPureComponent />\n          <br />\n          <br />\n          BrokenHooksComponent\n          <BrokenHooksComponent />\n          <br />\n          <br />\n          CorrectHooksComponent\n          <CorrectHooksComponent />\n          <br />\n          <br />\n          SmartHooksComponent\n          <SmartHooksComponent />\n        </div>\n      );\n    }\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/hotReload/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nconst text = 'change me when the app is running please';\n\nconst DemoComponent = ({children}) => (\n  <div>\n    <h4>{text}</h4>\n    {children}\n  </div>\n);\n\nDemoComponent.whyDidYouRender = true;\n\nexport default {\n  description: 'React Hot Reload Of Tracked Component',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    stepLogger('initial render');\n    reactDomRoot.render(<DemoComponent>yo!</DemoComponent>);\n\n    stepLogger('render with same props', true);\n    reactDomRoot.render(<DemoComponent>yo!</DemoComponent>);\n  },\n};\n"
  },
  {
    "path": "demo/src/index.js",
    "content": "import React from 'react';\nimport ReactDom from 'react-dom/client';\n\nimport App from './App';\n\nconst element = document.getElementById('menu');\n\nconst root = ReactDom.createRoot(element);\n\nroot.render(<App/>);\n\n"
  },
  {
    "path": "demo/src/logOwnerReasons/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Log Owner Reasons',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    const Child = () => null;\n    Child.whyDidYouRender = true;\n\n    const Owner = () => <Child />;\n\n    class ClassOwner extends React.Component {\n      state = {a: 1};\n      componentDidMount() {\n        this.setState({a: 2});\n      }\n\n      render() {\n        return <Child />;\n      }\n    }\n\n    function HooksOwner() {\n      /* eslint-disable no-unused-vars */\n      const [a, setA] = React.useState(1);\n      const [b, setB] = React.useState(1);\n      /* eslint-enable */\n      React.useEffect(() => {\n        setA(2);\n        setB(2);\n      }, []);\n\n      return <Child />;\n    }\n\n    stepLogger('First render');\n    reactDomRoot.render(<Owner a={1} />);\n\n    stepLogger('Owner props change', true);\n    reactDomRoot.render(<Owner a={2} />);\n\n    stepLogger('Owner state change', true);\n    reactDomRoot.render(<ClassOwner />);\n\n    stepLogger('Owner hooks changes', true);\n    reactDomRoot.render(<HooksOwner />);\n  },\n};\n"
  },
  {
    "path": "demo/src/noChanges/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'No Changes',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    class ClassDemo extends React.Component {\n      static whyDidYouRender = true;\n\n      componentDidMount() {\n        stepLogger('forceUpdate', true);\n        this.forceUpdate();\n      }\n      render() {\n        return <div>State And Props The Same</div>;\n      }\n    }\n\n    stepLogger('First Render');\n    reactDomRoot.render(<ClassDemo/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/propsChanges/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Props Changes',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    const ClassDemo = () => (\n      <div>Props Changes</div>\n    );\n    ClassDemo.whyDidYouRender = true;\n\n    const Main = props => (\n      <React.StrictMode>\n        <ClassDemo {...props}/>\n      </React.StrictMode>\n    );\n\n    stepLogger('First render');\n    reactDomRoot.render(<Main a={1} />);\n\n    stepLogger('Same props', true);\n    reactDomRoot.render(<Main a={1} />);\n\n    stepLogger('Other props');\n    reactDomRoot.render(<Main a={{b: 'b'}} />);\n\n    stepLogger('Different by ref, equals by value', true);\n    reactDomRoot.render(<Main a={{b: 'b'}} />);\n\n    stepLogger('Other nested props');\n    reactDomRoot.render(<Main a={{b: {c: {d: 'd'}}}} />);\n\n    stepLogger('Deep equal nested props', true);\n    reactDomRoot.render(<Main a={{b: {c: {d: 'd'}}}} />);\n\n    stepLogger('Mixed Props');\n    reactDomRoot.render(<Main containerProps={{style: {height: '100%'}, className: 'default-highchart'}} />);\n\n    stepLogger('Mixed Props again', true);\n    reactDomRoot.render(<Main containerProps={{style: {height: '100%'}, className: 'default-highchart'}} />);\n\n    const sameObj = {a: {b: 'c'}};\n\n    stepLogger('Mixed Props including eq obj');\n    reactDomRoot.render(<Main containerProps={{style: {height: '100%'}, className: 'default-highchart', sameObj}} />);\n\n    stepLogger('Mixed Props including eq obj', true);\n    reactDomRoot.render(<Main containerProps={{style: {height: '100%'}, className: 'default-highchart', sameObj}} />);\n  },\n};\n"
  },
  {
    "path": "demo/src/reactRedux/index.js",
    "content": "import React from 'react';\nimport _ from 'lodash';\nimport {createStore} from 'redux';\nimport * as Redux from 'react-redux';\n\nexport default {\n  description: 'React Redux',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React, {trackExtraHooks: [\n      [Redux, 'useSelector'],\n    ]});\n\n    const useDispatch = Redux.useDispatch;\n    const useSelector = Redux.useSelector;\n    const Provider = Redux.Provider;\n\n    const ConnectedSimpleComponent = () => {\n      const a = useSelector(state => state.a);\n      const dispatch = useDispatch();\n\n      return (\n        <div>\n          {`{a.b} is: ${a.b}`}\n          <br/>\n          <button onClick={() => dispatch({type: 'sameObj'})}>Same State</button>\n          <button onClick={() => dispatch({type: 'deepEqlObj'})}>Deep Equal State</button>\n          <button onClick={() => dispatch({type: 'randomObj'})}>Random Object</button>\n        </div>\n      );\n    };\n\n    ConnectedSimpleComponent.whyDidYouRender = true;\n\n    const initialState = {a: {b: 'c'}};\n    const store = createStore((state = initialState, action) => {\n      if (action.type === 'randomObj') {\n        return {a: {b: `${Math.random()}`}};\n      }\n      if (action.type === 'deepEqlObj') {\n        return _.cloneDeep(state);\n      }\n      return state;\n    });\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/reactReduxHOC/index.js",
    "content": "import React from 'react';\nimport {createStore} from 'redux';\nimport * as Redux from 'react-redux';\nimport _ from 'lodash';\n\nconst connect = Redux.connect;\nconst Provider = Redux.Provider;\n\nexport default {\n  description: 'React Redux HOC',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    const initialState = {a: {b: 'c'}};\n\n    const rootReducer = (state, action) => {\n      if (action.type === 'randomObj') {\n        return {a: {b: `${Math.random()}`}};\n      }\n\n      if (action.type === 'deepEqlObj') {\n        return _.cloneDeep(state);\n      }\n\n      return state;\n    };\n\n    const store = createStore(rootReducer, initialState);\n\n    const SimpleComponent = ({a, randomObj, deepEqlObj, sameObj}) => {\n      return (\n        <div>\n          {`{a.b} is: ${a.b}`}\n          <button onClick={sameObj}>Same State</button>\n          <button onClick={deepEqlObj}>Deep Equal State</button>\n          <button onClick={randomObj}>Random Object</button>\n        </div>\n      );\n    };\n\n    const ConnectedSimpleComponent = connect(\n      state => ({a: state.a}),\n      ({\n        randomObj: () => ({type: 'randomObj'}),\n        deepEqlObj: () => ({type: 'deepEqlObj'}),\n        sameObj: () => ({type: 'sameObj'}),\n      })\n    )(SimpleComponent);\n\n    SimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/specialChanges/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Special Changes',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    class ClassDemo extends React.Component {\n      static whyDidYouRender = true;\n\n      render() {\n        return <div>Special Changes</div>;\n      }\n    }\n\n    stepLogger('First render');\n    reactDomRoot.render(\n      <ClassDemo\n        regEx={/something/}\n        fn={function something() {}}\n        date={new Date('6/29/2011 4:52:48 PM UTC')}\n        reactElement={<div>hi!</div>}\n      />\n    );\n\n    stepLogger('Same special props', true);\n    reactDomRoot.render(\n      <ClassDemo\n        regEx={/something/}\n        fn={function something() {}}\n        date={new Date('6/29/2011 4:52:48 PM UTC')}\n        reactElement={<div>hi!</div>}\n      />\n    );\n  },\n};\n"
  },
  {
    "path": "demo/src/ssr/DemoComponent.js",
    "content": "const React = require('react');\nconst createReactClass = require('create-react-class');\n\nconst DemoComponent = createReactClass({\n  displayName: 'DemoComponent',\n  render() {\n    return React.createElement('div', {}, this.props.text);\n  },\n});\n\nDemoComponent.whyDidYouRender = true;\n\nmodule.exports = DemoComponent;\n"
  },
  {
    "path": "demo/src/ssr/index.js",
    "content": "import React from 'react';\nimport ReactDom from 'react-dom/client';\n\nimport createStepLogger from '../createStepLogger';\n\nimport DemoComponent from './DemoComponent';\n\nexport default {\n  description: 'Server Side (hydrate)',\n  fn({domElement, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    return fetch('/ssrComponent')\n      .then(response => response.text())\n      .then(initialDemoHTML => {\n        domElement.innerHTML = initialDemoHTML;\n\n        whyDidYouRender(React);\n\n        stepLogger('hydrate');\n        const hydratedRoot = ReactDom.hydrateRoot(domElement, <DemoComponent text=\"hydrated hi\"/>);\n\n        setTimeout(() => {\n          stepLogger('render with same props', true);\n          hydratedRoot.render(<DemoComponent text=\"hydrated hi\"/>);\n        }, 1);\n\n        return hydratedRoot;\n      });\n  },\n  settings: {shouldCreateRoot: false},\n};\n"
  },
  {
    "path": "demo/src/stateChanges/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'State Changes',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    class ClassDemo extends React.Component {\n      static whyDidYouRender = true;\n\n      state = {\n        stateKey: 'stateValue',\n      };\n\n      componentDidMount() {\n        stepLogger('Set an existing state key with the same value', true);\n        this.setState({stateKey: 'stateValue'}, () => {\n\n          stepLogger('Add object entry');\n          this.setState({objectKey: {a: 'a'}}, () => {\n\n            stepLogger('Add a new object entry that equals by value', true);\n            this.setState({objectKey: {a: 'a'}});\n          });\n        });\n      }\n\n      render() {\n        return <div>State Changes</div>;\n      }\n    }\n\n    stepLogger('First Render');\n    reactDomRoot.render(<ClassDemo a={1}/>);\n  },\n};\n"
  },
  {
    "path": "demo/src/strict/index.js",
    "content": "import React from 'react';\n\nimport createStepLogger from '../createStepLogger';\n\nexport default {\n  description: 'Strict mode',\n  fn({reactDomRoot, whyDidYouRender}) {\n    const stepLogger = createStepLogger();\n\n    whyDidYouRender(React);\n\n    class ClassDemo extends React.Component {\n      static whyDidYouRender = true;\n      render() {\n        return <div>Props Changes</div>;\n      }\n    }\n\n    const Main = props => (\n      <React.StrictMode>\n        <ClassDemo {...props}/>\n      </React.StrictMode>\n    );\n\n    stepLogger('First render');\n    reactDomRoot.render(<Main a={1} />);\n\n    stepLogger('Same props', true);\n    reactDomRoot.render(<Main a={1} />);\n\n    stepLogger('Other props');\n    reactDomRoot.render(<Main a={{b: 'b'}} />);\n\n    stepLogger('Different by ref, equals by value', true);\n    reactDomRoot.render(<Main a={{b: 'b'}} />);\n\n    stepLogger('Other nested props');\n    reactDomRoot.render(<Main a={{b: {c: {d: 'd'}}}} />);\n\n    stepLogger('Deep equal nested props', true);\n    reactDomRoot.render(<Main a={{b: {c: {d: 'd'}}}} />);\n  },\n};\n"
  },
  {
    "path": "demo/src/styledComponents/index.js",
    "content": "import React from 'react';\nimport styled from 'styled-components';\n\nexport default {\n  description: 'styled-components',\n  fn({reactDomRoot, whyDidYouRender}) {\n    whyDidYouRender(React);\n\n    const SimpleComponent = (props) => {\n      return (\n        <div {...props}>\n          styled-components\n        </div>\n      );\n    };\n\n    const StyledSimpleComponent = styled(SimpleComponent)`\n      background-color: #ff96ae;\n      font-style: italic;\n    `;\n\n    StyledSimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <StyledSimpleComponent a={[]}/>\n    );\n\n    reactDomRoot.render(<Main/>);\n    reactDomRoot.render(<Main/>);\n  },\n};\n"
  },
  {
    "path": "eslint.config.js",
    "content": "const reactPlugin = require('eslint-plugin-react');\nconst js = require('@eslint/js');\nconst globals = require('globals');\nconst {includeIgnoreFile} = require('@eslint/compat');\nconst pluginCypress = require('eslint-plugin-cypress/flat');\n\n// TODO: remove once all deps are using the latest version\nglobals.browser['AudioWorkletGlobalScope'] = globals.browser['AudioWorkletGlobalScope '];\ndelete globals.browser['AudioWorkletGlobalScope '];\n\n\nmodule.exports = [\n  includeIgnoreFile(__dirname + '/.gitignore'),\n  js.configs.recommended,\n  pluginCypress.configs.globals,\n  {\n    plugins: {\n      cypress: pluginCypress\n    },\n    rules: {\n      'cypress/unsafe-to-chain-command': 'error'\n    },\n  },\n  {\n    ...reactPlugin.configs.flat.recommended,\n    languageOptions: {\n      ...reactPlugin.configs.flat.recommended.languageOptions,\n      globals: {\n        ...globals.browser,\n        ...globals.jest,\n        ...globals.node,\n        ...globals.console,\n        flushConsoleOutput: 'readable',\n      },\n    },\n    rules: {\n      'semi': ['error', 'always'],\n      'curly': 'error',\n      'no-var': 'error',\n      'quotes': ['error', 'single'],\n      'no-console': 'error',\n      'no-debugger': 'warn',\n      'react/jsx-uses-vars': 'error',\n      'react/jsx-uses-react': 'error',\n      'no-unused-vars': ['error', {\n        'ignoreRestSiblings': true,\n        'varsIgnorePattern': '^_',\n        'argsIgnorePattern': '^_',\n        'caughtErrorsIgnorePattern': '^_',\n        'destructuredArrayIgnorePattern': '^_'\n      }],\n      'eol-last': 'error',\n      'object-curly-spacing': ['error', 'never'],\n      'react/prop-types': 'off',\n      'react/display-name': 'off',\n      'space-before-function-paren': ['error', 'never'],\n      'space-before-blocks': ['error', 'always'],\n      'space-in-parens': ['error', 'never'],\n      'comma-dangle': ['error', 'only-multiline'],\n      'func-call-spacing': ['error', 'never'],\n      'no-multi-spaces': 'error',\n      'indent': ['error', 2]\n    }\n  }\n];\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  cacheDirectory: '.cache/jest-cache',\n  setupFiles: ['./jest.polyfills.js'],\n  setupFilesAfterEnv: [\n    '<rootDir>/jestSetup.js',\n  ],\n  moduleNameMapper: {\n    '~(.*)$': '<rootDir>/src$1',\n    '^@welldone-software/why-did-you-render$': '<rootDir>/src/whyDidYouRender.js',\n  },\n  testEnvironment: 'jsdom',\n  extensionsToTreatAsEsm: ['.ts', '.tsx'],\n};\n"
  },
  {
    "path": "jest.polyfills.js",
    "content": "// jest.polyfills.js\n/**\n * @note The block below contains polyfills for Node.js globals\n * required for Jest to function when running JSDOM tests.\n * These HAVE to be require's and HAVE to be in this exact\n * order, since \"undici\" depends on the \"TextEncoder\" global API.\n *\n * Consider migrating to a more modern test runner if\n * you don't want to deal with this.\n */\n \nconst {TextDecoder, TextEncoder} = require('node:util');\n \nObject.defineProperties(globalThis, {\n  TextDecoder: {value: TextDecoder},\n  TextEncoder: {value: TextEncoder},\n});\n \nconst {Blob, File} = require('node:buffer'); \n \nObject.defineProperties(globalThis, {\n  Blob: {value: Blob},\n  File: {value: File},\n});\n\nwindow.MessageChannel = jest.fn().mockImplementation(() => {\n  let onmessage;\n  return {\n    port1: {\n      set onmessage(cb) {\n        onmessage = cb;\n      },\n    },\n    port2: {\n      postMessage: data => {\n        if (onmessage) {\n          onmessage({data});\n        }\n      },\n    },\n  };\n});\n"
  },
  {
    "path": "jestSetup.js",
    "content": "import {errorOnConsoleOutput} from '@welldone-software/jest-console-handler';\n\nconst substringsToIgnore = [\n  'Selectors that return the entire state are almost certainly a mistake',\n  'Warning: ReactDOM.render is no longer supported in React 19',\n  'Support for defaultProps will be removed from',\n];\nconst regexToIgnore = new RegExp(`(${substringsToIgnore.join('|')})`);\n\nglobal.flushConsoleOutput = errorOnConsoleOutput({filterEntries: ({args}) => {\n  const shouldIgnoreConsoleLog = regexToIgnore.test(args[0]);\n  return !shouldIgnoreConsoleLog;\n}});\n\nconst React = require('react');\nif (!React.version.startsWith('19')) {\n  throw new Error(`Wrong React version. Expected ^19, got ${React.version}`);\n}\n"
  },
  {
    "path": "jsx-dev-runtime.d.ts",
    "content": "import './types.d.ts';\n"
  },
  {
    "path": "jsx-dev-runtime.js",
    "content": "/* eslint-disable*/\nvar jsxDevRuntime = require('react/jsx-dev-runtime')\nvar WDYR = require('@welldone-software/why-did-you-render')\n\nvar origJsxDev = jsxDevRuntime.jsxDEV\nvar wdyrStore = WDYR.wdyrStore\n\nmodule.exports = {\n  ...jsxDevRuntime,\n  jsxDEV(...args) {\n    if (wdyrStore.React && wdyrStore.React.__IS_WDYR__) {\n      var origType = args[0]\n      var rest = args.slice(1)\n  \n      var WDYRType = WDYR.getWDYRType(origType)\n      if (WDYRType) {\n        try {\n          wdyrStore.ownerBeforeElementCreation = WDYR.getCurrentOwner();\n          var element = origJsxDev.apply(null, [WDYRType].concat(rest))\n          if (wdyrStore.options.logOwnerReasons) {\n            WDYR.storeOwnerData(element)\n          }\n          return element\n        } catch(e) {\n          wdyrStore.options.consoleLog('whyDidYouRender JSX transform error. Please file a bug at https://github.com/welldone-software/why-did-you-render/issues.', {\n            errorInfo: {\n              error: e,\n              componentNameOrComponent: origType,\n              rest: rest,\n              options: wdyrStore.options\n            }\n          })\n        }\n      }\n    }\n    \n    return origJsxDev.apply(null, args)\n  }\n};\n"
  },
  {
    "path": "jsx-runtime.d.ts",
    "content": "import './types.d.ts';\n"
  },
  {
    "path": "jsx-runtime.js",
    "content": "module.exports = require('react/jsx-runtime');\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@welldone-software/why-did-you-render\",\n  \"description\": \"Monkey patches React to notify you about avoidable re-renders.\",\n  \"version\": \"10.0.1\",\n  \"repository\": \"git+https://github.com/welldone-software/why-did-you-render.git\",\n  \"license\": \"MIT\",\n  \"authors\": [\n    \"Vitali Zaidman <vzaidman@gmail.com> (https://github.com/vzaidman)\"\n  ],\n  \"types\": \"types.d.ts\",\n  \"main\": \"dist/whyDidYouRender.js\",\n  \"files\": [\n    \"dist\",\n    \"types.d.ts\",\n    \"jsx-runtime.js\",\n    \"jsx-runtime.d.ts\",\n    \"jsx-dev-runtime.js\",\n    \"jsx-dev-runtime.d.ts\"\n  ],\n  \"keywords\": [\n    \"react\",\n    \"component\",\n    \"pure\",\n    \"performance\",\n    \"render\",\n    \"update\",\n    \"tool\"\n  ],\n  \"scripts\": {\n    \"start\": \"cross-env PORT=3003 NODE_ENV=development node demo/serve\",\n    \"build\": \"cross-env NODE_ENV=production rollup --config --bundleConfigAsCjs\",\n    \"test\": \"jest --config=jest.config.js\",\n    \"test:ci\": \"yarn test --coverage\",\n    \"lint\": \"eslint . --max-warnings 0 --cache --cache-location .cache/eslint-cache\",\n    \"clear\": \"rimraf .cache dist demo/dist node_modules\",\n    \"watch\": \"concurrently --names \\\"Serve,Test\\\" \\\"npm:start\\\" \\\"npm:test:watch\\\"\",\n    \"checkHealth\": \"yarn build && yarn lint && yarn test && yarn cypress:test\",\n    \"version\": \"yarn checkHealth\",\n    \"postversion\": \"git push && git push --tags\",\n    \"cypress:open\": \"cypress open\",\n    \"cypress:run\": \"cypress run --browser chrome\",\n    \"cypress:test\": \"start-server-and-test start http://localhost:3003 cypress:run\"\n  },\n  \"comments\": {\n    \"how to\": {\n      \"bump version\": \"npm version major/minor/patch\"\n    },\n    \"resolutions\": {\n      \"source-map@^0.7.4\": [\n        \"fixes https://github.com/mozilla/source-map/issues/432 or we get:\",\n        \"forces nollup to use source-map 0.8.0-beta.0 or higher.\",\n        \"will be resolved when nollup is updated to use it\"\n      ],\n      \"rollup-plugin-react-refresh\": [\n        \"Uses my forked github https://github.com/vzaidman/rollup-plugin-react-refresh\",\n        \"Until the PR from that is merged to the official library https://github.com/PepsRyuu/rollup-plugin-react-refresh/pull/10\"\n      ]\n    }\n  },\n  \"dependencies\": {\n    \"lodash\": \"^4\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^19\"\n  },\n  \"resolutions\": {\n    \"source-map-fast\": \"npm:source-map@^0.8.0-beta.0\",\n    \"source-map\": \"^0.8.0-beta.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.26.0\",\n    \"@babel/preset-env\": \"^7.26.0\",\n    \"@babel/preset-react\": \"^7.26.3\",\n    \"@eslint/compat\": \"^1.2.4\",\n    \"@rollup/plugin-alias\": \"^5.1.1\",\n    \"@rollup/plugin-babel\": \"^6.0.4\",\n    \"@rollup/plugin-commonjs\": \"^28.0.2\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.0\",\n    \"@rollup/plugin-replace\": \"^6.0.2\",\n    \"@testing-library/dom\": \"^10.4.0\",\n    \"@testing-library/jest-dom\": \"^6.6.3\",\n    \"@testing-library/react\": \"^16.1.0\",\n    \"@types/jest\": \"^29.5.14\",\n    \"@types/react\": \"^19.0.2\",\n    \"@types/react-dom\": \"^19.0.2\",\n    \"@types/react-redux\": \"^7.1.34\",\n    \"@welldone-software/jest-console-handler\": \"^2.0.1\",\n    \"acorn-walk\": \"^8.3.4\",\n    \"astring\": \"^1.9.0\",\n    \"babel-core\": \"^7.0.0-bridge.0\",\n    \"babel-jest\": \"^29.7.0\",\n    \"concurrently\": \"^9.1.1\",\n    \"create-react-class\": \"^15.7.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"cypress\": \"^13.17.0\",\n    \"eslint\": \"^9.17.0\",\n    \"eslint-plugin-cypress\": \"^4.1.0\",\n    \"eslint-plugin-jest\": \"^28.10.0\",\n    \"eslint-plugin-react\": \"^7.37.3\",\n    \"express\": \"^4.21.2\",\n    \"express-history-api-fallback\": \"^2.2.1\",\n    \"husky\": \"^9.1.7\",\n    \"jest\": \"^29.7.0\",\n    \"jest-cli\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"nollup\": \"^0.21.0\",\n    \"react\": \"^19.0.0\",\n    \"react-dom\": \"^19.0.0\",\n    \"react-is\": \"^19.0.0\",\n    \"react-redux\": \"^9.2.0\",\n    \"react-refresh\": \"^0.16.0\",\n    \"react-router-dom\": \"^7.1.1\",\n    \"redux\": \"^5.0.1\",\n    \"rimraf\": \"^6.0.1\",\n    \"rollup\": \"^4.29.1\",\n    \"rollup-plugin-alias\": \"^2.2.0\",\n    \"rollup-plugin-commonjs-alternate\": \"^0.8.0\",\n    \"rollup-plugin-license\": \"^3.5.3\",\n    \"rollup-plugin-node-resolve\": \"^5.2.0\",\n    \"rollup-plugin-react-refresh\": \"https://github.com/vzaidman/rollup-plugin-react-refresh.git#5c2f09bc28dbb8ab711b7d095f61fbc8d295fcd6\",\n    \"source-map\": \"npm:source-map@^0.8.0-beta.0\",\n    \"start-server-and-test\": \"^2.0.9\",\n    \"styled-components\": \"^6.1.13\",\n    \"typescript\": \"^5.7.2\"\n  }\n}"
  },
  {
    "path": "rollup.config.js",
    "content": "import fs from 'fs';\nimport resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport babel from '@rollup/plugin-babel';\nimport license from 'rollup-plugin-license';\n\nconst loadJSON = (path) => JSON.parse(fs.readFileSync(new URL(path, import.meta.url)));\n\nconst pkg = loadJSON('./package.json');\n\nconst banner = `\n<%= pkg.name %> <%= pkg.version %>\nMIT Licensed \nGenerated by <%= pkg.authors[0] %>\nGenerated at <%= moment().format('YYYY-MM-DD') %>\n`;\n\nexport default [\n  {\n    input: 'src/index.js',\n    external: ['lodash', 'react'],\n    output: [\n      {\n        name: 'whyDidYouRender',\n        file: pkg.main,\n        format: 'umd',\n        sourcemap: true,\n        exports: 'default',\n        globals: {\n          lodash: 'lodash',\n          react: 'react',\n        },\n      },\n    ],\n    plugins: [\n      babel({\n        exclude: 'node_modules/**',\n        babelHelpers: 'bundled',\n      }),\n      resolve(),\n      commonjs(),\n      license({\n        sourcemap: true,\n        banner,\n      }),\n    ],\n  },\n];\n"
  },
  {
    "path": "src/calculateDeepEqualDiffs.js",
    "content": "import {\n  isArray,\n  isPlainObject,\n  isDate,\n  isRegExp,\n  isError,\n  isFunction,\n  isSet,\n  has,\n  uniq,\n} from 'lodash';\n\nimport {diffTypes} from './consts';\n\nconst hasElementType = typeof Element !== 'undefined';\n\n// copied from https://github.com/facebook/react/blob/fc5ef50da8e975a569622d477f1fed54cb8b193d/packages/react-devtools-shared/src/backend/shared/ReactSymbols.js#L26\nconst hasSymbol = typeof Symbol === 'function' && Symbol.for;\n\nconst LEGACY_ELEMENT_NUMBER = 0xeac7;\nconst LEGACY_ELEMENT_SYMBOL_STRING = hasSymbol && Symbol.for('react.element');\nconst ELEMENT_SYMBOL_STRING = hasSymbol && Symbol.for('react.transitional.element');\nconst isReactElement = object => [\n  ...(hasSymbol ? [ELEMENT_SYMBOL_STRING, LEGACY_ELEMENT_SYMBOL_STRING] : []),\n  LEGACY_ELEMENT_NUMBER,\n].includes(object.$$typeof);\n// end\n\nfunction trackDiff(a, b, diffsAccumulator, pathString, diffType) {\n  diffsAccumulator.push({\n    diffType,\n    pathString,\n    prevValue: a,\n    nextValue: b,\n  });\n  return diffType !== diffTypes.different;\n}\n\nfunction isGetter(obj, prop) {\n  return !!Object.getOwnPropertyDescriptor(obj, prop)['get'];\n}\n\nexport const dependenciesMap = new WeakMap();\n\nfunction accumulateDeepEqualDiffs(a, b, diffsAccumulator, pathString = '', {detailed}) {\n  if (a === b) {\n    if (detailed) {\n      trackDiff(a, b, diffsAccumulator, pathString, diffTypes.same);\n    }\n    return true;\n  }\n\n  if (!a || !b) {\n    return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (isArray(a) && isArray(b)) {\n    const arrayLength = a.length;\n    if (arrayLength !== b.length) {\n      return trackDiff([...a], [...b], diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    const arrayItemDiffs = [];\n    let numberOfDeepEqualsItems = 0;\n    for (let i = arrayLength; i--; i > 0) {\n      const diffEquals = accumulateDeepEqualDiffs(a[i], b[i], arrayItemDiffs, `${pathString}[${i}]`, {detailed});\n      if (diffEquals) {\n        numberOfDeepEqualsItems++;\n      }\n    }\n\n    if (detailed || numberOfDeepEqualsItems !== arrayLength) {\n      diffsAccumulator.push(...arrayItemDiffs);\n    }\n\n    if (numberOfDeepEqualsItems === arrayLength) {\n      return trackDiff([...a], [...b], diffsAccumulator, pathString, diffTypes.deepEquals);\n    }\n\n    return trackDiff([...a], [...b], diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (isSet(a) && isSet(b)) {\n    if (a.size !== b.size) {\n      return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    for (const valA of a) {\n      if (!b.has(valA)) {\n        return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.different);\n      }\n    }\n\n    return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.deepEquals);\n  }\n\n  if (isDate(a) && isDate(b)) {\n    return a.getTime() === b.getTime() ?\n      trackDiff(new Date(a), new Date(b), diffsAccumulator, pathString, diffTypes.date) :\n      trackDiff(new Date(a), new Date(b), diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (isRegExp(a) && isRegExp(b)) {\n    return a.toString() === b.toString() ?\n      trackDiff(a, b, diffsAccumulator, pathString, diffTypes.regex) :\n      trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (hasElementType && a instanceof Element && b instanceof Element) {\n    return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (isReactElement(a) && isReactElement(b)) {\n    if (a.type !== b.type) {\n      return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    const reactElementPropsAreDeepEqual =\n      accumulateDeepEqualDiffs(a.props, b.props, [], `${pathString}.props`, {detailed});\n\n    return reactElementPropsAreDeepEqual ?\n      trackDiff(a, b, diffsAccumulator, pathString, diffTypes.reactElement) :\n      trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  if (isFunction(a) && isFunction(b)) {\n    if (a.name !== b.name) {\n      return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    const aDependenciesObj = dependenciesMap.get(a);\n    const bDependenciesObj = dependenciesMap.get(b);\n\n    if (aDependenciesObj && bDependenciesObj) {\n      const dependenciesAreDeepEqual =\n        accumulateDeepEqualDiffs(aDependenciesObj.deps, bDependenciesObj.deps, diffsAccumulator, `${pathString}:parent-hook-${aDependenciesObj.hookName}-deps`, {detailed});\n\n      return dependenciesAreDeepEqual ?\n        trackDiff(a, b, diffsAccumulator, pathString, diffTypes.function) :\n        trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.function);\n  }\n\n  if (typeof a === 'object' && typeof b === 'object' && Object.getPrototypeOf(a) === Object.getPrototypeOf(b)) {\n    const aKeys = Object.getOwnPropertyNames(a);\n    const bKeys = Object.getOwnPropertyNames(b);\n    \n    const allKeys = uniq([...aKeys, ...bKeys]);\n\n    const clonedA = isPlainObject(a) ? {...a} : a;\n    const clonedB = isPlainObject(b) ? {...b} : b;\n\n    if (allKeys.length !== aKeys.length || allKeys.length !== bKeys.length) {\n      return trackDiff(clonedA, clonedB, diffsAccumulator, pathString, diffTypes.different);\n    }\n\n    const relevantKeys = allKeys.filter(key => {\n      // do not compare the stack as it differ even though the errors are identical.\n      if (key === 'stack' && isError(a)) {\n        return false;\n      }\n\n      // getters checking is causing too much problems because of how it's used in js.\n      // not only getters can throw errors, they also cause side effects in many cases.\n      if (isGetter(a, key)) {\n        return false;\n      }\n\n      return true;\n    });\n\n    const keysLength = relevantKeys.length;\n\n    for (let i = keysLength; i--; i > 0) {\n      if (!has(b, relevantKeys[i])) {\n        return trackDiff(clonedA, clonedB, diffsAccumulator, pathString, diffTypes.different);\n      }\n    }\n\n    const objectValuesDiffs = [];\n    let numberOfDeepEqualsObjectValues = 0;\n    for (let i = keysLength; i--; i > 0) {\n      const key = relevantKeys[i];\n      const deepEquals = accumulateDeepEqualDiffs(a[key], b[key], objectValuesDiffs, `${pathString}.${key}`, {detailed});\n      if (deepEquals) {\n        numberOfDeepEqualsObjectValues++;\n      }\n    }\n\n    if (detailed || numberOfDeepEqualsObjectValues !== keysLength) {\n      diffsAccumulator.push(...objectValuesDiffs);\n    }\n\n    if (numberOfDeepEqualsObjectValues === keysLength) {\n      return trackDiff(clonedA, clonedB, diffsAccumulator, pathString, diffTypes.deepEquals);\n    }\n\n    return trackDiff(clonedA, clonedB, diffsAccumulator, pathString, diffTypes.different);\n  }\n\n  return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different);\n}\n\nexport default function calculateDeepEqualDiffs(a, b, initialPathString, {detailed = false} = {}) {\n  try {\n    const diffs = [];\n    accumulateDeepEqualDiffs(a, b, diffs, initialPathString, {detailed});\n    return diffs;\n  } catch (error) {\n    if ((error.message && error.message.match(/stack|recursion/i)) || (error.number === -2146828260)) {\n      // warn on circular references, don't crash.\n      // browsers throw different errors name and messages:\n      // chrome/safari: \"RangeError\", \"Maximum call stack size exceeded\"\n      // firefox: \"InternalError\", too much recursion\"\n      // edge: \"Error\", \"Out of stack space\"\n      // eslint-disable-next-line no-console\n      console.warn('Warning: why-did-you-render couldn\\'t handle circular references in props.', error.name, error.message);\n      return false;\n    }\n    throw error;\n  }\n}\n"
  },
  {
    "path": "src/consts.js",
    "content": "export const diffTypes = {\n  'different': 'different',\n  'deepEquals': 'deepEquals',\n  'date': 'date',\n  'regex': 'regex',\n  'reactElement': 'reactElement',\n  'function': 'function',\n  'same': 'same',\n};\n\nexport const diffTypesDescriptions = {\n  [diffTypes.different]: 'different objects',\n  [diffTypes.deepEquals]: 'different objects that are equal by value',\n  [diffTypes.date]: 'different date objects with the same value',\n  [diffTypes.regex]: 'different regular expressions with the same value',\n  [diffTypes.reactElement]: 'different React elements (remember that the <jsx/> syntax always produces a *NEW* immutable React element so a component that receives <jsx/> as props always re-renders)',\n  [diffTypes.function]: 'different functions with the same name',\n  [diffTypes.same]: 'same objects by ref (===)',\n};\n\n// copied from packages/shared/ReactSymbols.js in https://github.com/facebook/react\nconst hasSymbol = typeof Symbol === 'function' && Symbol.for;\nexport const REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;\nexport const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;\nexport const REACT_STRICT_MODE = 0b1000;\n"
  },
  {
    "path": "src/defaultNotifier.js",
    "content": "import wdyrStore from './wdyrStore';\n\nimport {diffTypes, diffTypesDescriptions} from './consts';\nimport printDiff from './printDiff';\n\nconst moreInfoUrl = 'http://bit.ly/wdyr02';\nconst moreInfoHooksUrl = 'http://bit.ly/wdyr3';\n\nlet inHotReload = false;\n\nfunction shouldLog(reason, Component) {\n  if (inHotReload) {\n    return false;\n  }\n\n  if (wdyrStore.options.logOnDifferentValues) {\n    return true;\n  }\n\n  if (Component.whyDidYouRender && Component.whyDidYouRender.logOnDifferentValues) {\n    return true;\n  }\n\n  const hasDifferentValues = ((\n    reason.propsDifferences &&\n    reason.propsDifferences.some(diff => diff.diffType === diffTypes.different)\n  ) || (\n    reason.stateDifferences &&\n    reason.stateDifferences.some(diff => diff.diffType === diffTypes.different)\n  ) || (\n    reason.hookDifferences &&\n    reason.hookDifferences.some(diff => diff.diffType === diffTypes.different)\n  ));\n\n  return !hasDifferentValues;\n}\n\nfunction logDifference({Component, displayName, hookName, prefixMessage, diffObjType, differences, values}) {\n  if (differences && differences.length > 0) {\n    wdyrStore.options.consoleLog({[displayName]: Component}, `${prefixMessage} of ${diffObjType} changes:`);\n    differences.forEach(({pathString, diffType, prevValue, nextValue}) => {\n      function diffFn() {\n        printDiff(prevValue, nextValue, {pathString, consoleLog: wdyrStore.options.consoleLog});\n      }\n      wdyrStore.options.consoleGroup(\n        `%c${diffObjType === 'hook' ? `[hook ${hookName} result]` : `${diffObjType}.`}%c${pathString}%c`,\n        `background-color: ${wdyrStore.options.textBackgroundColor};color:${wdyrStore.options.diffNameColor};`,\n        `background-color: ${wdyrStore.options.textBackgroundColor};color:${wdyrStore.options.diffPathColor};`,\n        'background-color: ${wdyrStore.options.textBackgroundColor};color:default;'\n      );\n      wdyrStore.options.consoleLog(\n        `${diffTypesDescriptions[diffType]}. (more info at ${hookName ? moreInfoHooksUrl : moreInfoUrl})`,\n      );\n      wdyrStore.options.consoleLog({[`prev ${pathString}`]: prevValue}, '!==', {[`next ${pathString}`]: nextValue});\n      if (diffType === diffTypes.deepEquals) {\n        wdyrStore.options.consoleLog({'For detailed diff, right click the following fn, save as global, and run: ': diffFn});\n      }\n      wdyrStore.options.consoleGroupEnd();\n    });\n  }\n  else if (differences) {\n    wdyrStore.options.consoleLog(\n      {[displayName]: Component},\n      `${prefixMessage} the ${diffObjType} object itself changed but its values are all equal.`,\n      diffObjType === 'props' ?\n        'This could have been avoided by making the component pure, or by preventing its father from re-rendering.' :\n        'This usually means this component called setState when no changes in its state actually occurred.',\n      `More info at ${moreInfoUrl}`\n    );\n    wdyrStore.options.consoleLog(`prev ${diffObjType}:`, values.prev, ' !== ', values.next, `:next ${diffObjType}`);\n  }\n}\n\nexport default function defaultNotifier(updateInfo) {\n  const {Component, displayName, hookName, prevOwner, nextOwner, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason} = updateInfo;\n\n  if (!shouldLog(reason, Component, wdyrStore.options)) {\n    return;\n  }\n\n  wdyrStore.options.consoleGroup(`%c${displayName}`, `background-color: ${wdyrStore.options.textBackgroundColor};color: ${wdyrStore.options.titleColor};`);\n\n  let prefixMessage = 'Re-rendered because';\n\n  if (reason.propsDifferences) {\n    logDifference({\n      Component,\n      displayName,\n      prefixMessage,\n      diffObjType: 'props',\n      differences: reason.propsDifferences,\n      values: {prev: prevProps, next: nextProps},\n    });\n    prefixMessage = 'And because';\n  }\n\n  if (reason.stateDifferences) {\n    logDifference({\n      Component,\n      displayName,\n      prefixMessage,\n      diffObjType: 'state',\n      differences: reason.stateDifferences,\n      values: {prev: prevState, next: nextState},\n    });\n  }\n\n  if (reason.hookDifferences) {\n    logDifference({\n      Component,\n      displayName,\n      prefixMessage,\n      diffObjType: 'hook',\n      differences: reason.hookDifferences,\n      values: {prev: prevHookResult, next: nextHookResult},\n      hookName,\n    });\n  }\n\n  if (reason.propsDifferences && reason.ownerDifferences) {\n    const prevOwnerData = wdyrStore.ownerDataMap.get(prevOwner);\n    const nextOwnerData = wdyrStore.ownerDataMap.get(nextOwner);\n\n    if (prevOwnerData && nextOwnerData) {\n      wdyrStore.options.consoleGroup(`Rendered by ${nextOwnerData.displayName}`);\n      let prefixMessage = 'Re-rendered because';\n  \n      if (reason.ownerDifferences.propsDifferences) {\n        logDifference({\n          Component: nextOwnerData.Component,\n          displayName: nextOwnerData.displayName,\n          prefixMessage,\n          diffObjType: 'props',\n          differences: reason.ownerDifferences.propsDifferences,\n          values: {prev: prevOwnerData.props, next: nextOwnerData.props},\n        });\n        prefixMessage = 'And because';\n      }\n  \n      if (reason.ownerDifferences.stateDifferences) {\n        logDifference({\n          Component: nextOwnerData.Component,\n          displayName: nextOwnerData.displayName,\n          prefixMessage,\n          diffObjType: 'state',\n          differences: reason.ownerDifferences.stateDifferences,\n          values: {prev: prevOwnerData.state, next: nextOwnerData.state},\n        });\n      }\n  \n      if (reason.ownerDifferences.hookDifferences) {\n        reason.ownerDifferences.hookDifferences.forEach(({hookName, differences}, i) =>\n          logDifference({\n            Component: nextOwnerData.Component,\n            displayName: nextOwnerData.displayName,\n            prefixMessage,\n            diffObjType: 'hook',\n            differences,\n            values: {prev: prevOwnerData.hooksInfo[i].result, next: nextOwnerData.hooksInfo[i].result},\n            hookName,\n          })\n        );\n      }\n      wdyrStore.options.consoleGroupEnd();\n    }\n  }\n\n  if (!reason.propsDifferences && !reason.stateDifferences && !reason.hookDifferences) {\n    wdyrStore.options.consoleLog(\n      {[displayName]: Component},\n      'Re-rendered although props and state objects are the same.',\n      'This usually means there was a call to this.forceUpdate() inside the component.',\n      `more info at ${moreInfoUrl}`\n    );\n  }\n\n  wdyrStore.options.consoleGroupEnd();\n}\n\nexport function createDefaultNotifier(hotReloadBufferMs) {\n  if (hotReloadBufferMs) {\n    if (typeof(module) !== 'undefined' && module.hot && module.hot.addStatusHandler) {\n      module.hot.addStatusHandler(status => {\n        if (status === 'idle') {\n          inHotReload = true;\n          setTimeout(() => {\n            inHotReload = false;\n          }, hotReloadBufferMs);\n        }\n      });\n    }\n  }\n\n  return defaultNotifier;\n}\n"
  },
  {
    "path": "src/findObjectsDifferences.js",
    "content": "import {reduce} from 'lodash';\nimport calculateDeepEqualDiffs from './calculateDeepEqualDiffs';\n\nconst emptyObject = {};\n\nexport default function findObjectsDifferences(userPrevObj, userNextObj, {shallow = true} = {}) {\n  if (userPrevObj === userNextObj) {\n    return false;\n  }\n\n  if (!shallow) {\n    return calculateDeepEqualDiffs(userPrevObj, userNextObj);\n  }\n\n  const prevObj = userPrevObj || emptyObject;\n  const nextObj = userNextObj || emptyObject;\n\n  const keysOfBothObjects = Object.keys({...prevObj, ...nextObj});\n\n  return reduce(keysOfBothObjects, (result, key) => {\n    const deepEqualDiffs = calculateDeepEqualDiffs(prevObj[key], nextObj[key], key);\n    if (deepEqualDiffs) {\n      result = [\n        ...result,\n        ...deepEqualDiffs,\n      ];\n    }\n    return result;\n  }, []);\n}\n"
  },
  {
    "path": "src/getDefaultProps.js",
    "content": "export default function getDefaultProps(type) {\n  return (\n    type.defaultProps ||\n    (type.type && getDefaultProps(type.type)) ||\n    (type.render && getDefaultProps(type.render)) ||\n    undefined\n  );\n}\n"
  },
  {
    "path": "src/getDisplayName.js",
    "content": "import {isString} from 'lodash';\n\nexport default function getDisplayName(type) {\n  return (\n    type.displayName ||\n    type.name ||\n    (type.type && getDisplayName(type.type)) ||\n    (type.render && getDisplayName(type.render)) ||\n    (isString(type) ? type : 'Unknown')\n  );\n}\n"
  },
  {
    "path": "src/getUpdateInfo.js",
    "content": "import findObjectsDifferences from './findObjectsDifferences';\nimport wdyrStore from './wdyrStore';\n\nfunction getOwnerDifferences(prevOwner, nextOwner) {\n  if (!prevOwner || !nextOwner) {\n    return false;\n  }\n\n  const prevOwnerData = wdyrStore.ownerDataMap.get(prevOwner);\n  const nextOwnerData = wdyrStore.ownerDataMap.get(nextOwner);\n\n  if (!prevOwnerData || !nextOwnerData) {\n    return false;\n  }\n\n  try {\n    // in strict mode a re-render happens twice as opposed to the initial render that happens once.\n    const prevOwnerDataHooks = prevOwnerData.hooksInfo.length === nextOwnerData.hooksInfo.length * 2 ?\n      prevOwnerData.hooksInfo.slice(prevOwnerData.hooksInfo.length / 2) :\n      prevOwnerData.hooksInfo;\n\n    const hookDifferences = prevOwnerDataHooks.map(({hookName, result}, i) => ({\n      hookName,\n      differences: findObjectsDifferences(result, nextOwnerData.hooksInfo[i].result, {shallow: false}),\n    }));\n\n    return {\n      propsDifferences: findObjectsDifferences(prevOwnerData.props, nextOwnerData.props),\n      stateDifferences: findObjectsDifferences(prevOwnerData.state, nextOwnerData.state),\n      hookDifferences: hookDifferences.length > 0 ? hookDifferences : false,\n    };\n  }\n  catch(e) {\n    wdyrStore.options.consoleLog('whyDidYouRender error in getOwnerDifferences. Please file a bug at https://github.com/welldone-software/why-did-you-render/issues.', {\n      errorInfo: {\n        error: e,\n        prevOwner,\n        nextOwner,\n        options: wdyrStore.options,\n      },\n    });\n    return false;\n  }\n}\n\nfunction getUpdateReason(prevOwner, prevProps, prevState, prevHookResult, nextOwner, nextProps, nextState, nextHookResult) {\n  return {\n    propsDifferences: findObjectsDifferences(prevProps, nextProps),\n    stateDifferences: findObjectsDifferences(prevState, nextState),\n    hookDifferences: findObjectsDifferences(prevHookResult, nextHookResult, {shallow: false}),\n    ownerDifferences: getOwnerDifferences(prevOwner, nextOwner),\n  };\n}\n\nexport default function getUpdateInfo({Component, displayName, hookName, prevOwner, nextOwner, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult}) {\n  return {\n    Component,\n    displayName,\n    hookName,\n    prevOwner,\n    prevProps,\n    prevState,\n    prevHookResult,\n    nextOwner,\n    nextProps,\n    nextState,\n    nextHookResult,\n    reason: getUpdateReason(prevOwner, prevProps, prevState, prevHookResult, nextOwner, nextProps, nextState, nextHookResult),\n    ownerDataMap: wdyrStore.ownerDataMap,\n  };\n}\n"
  },
  {
    "path": "src/helpers.js",
    "content": "import wdyrStore from './wdyrStore';\n\nexport function getCurrentOwner() {\n  const reactSharedInternals = wdyrStore.React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;\n  const reactDispatcher = reactSharedInternals?.A;\n  return reactDispatcher?.getOwner();\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import * as React from 'react';\n\nimport wdyrStore from './wdyrStore';\n\nimport whyDidYouRender, {storeOwnerData, getWDYRType} from './whyDidYouRender';\nimport defaultNotifier from './defaultNotifier';\nimport {getCurrentOwner} from './helpers';\n\nwhyDidYouRender.defaultNotifier = defaultNotifier;\nwhyDidYouRender.wdyrStore = wdyrStore;\nwhyDidYouRender.storeOwnerData = storeOwnerData;\nwhyDidYouRender.getWDYRType = getWDYRType;\nwhyDidYouRender.getCurrentOwner = getCurrentOwner;\nObject.assign(whyDidYouRender, React);\n\nexport default whyDidYouRender;\n"
  },
  {
    "path": "src/normalizeOptions.js",
    "content": "/* eslint-disable no-console */\nimport {createDefaultNotifier} from './defaultNotifier';\n\nconst emptyFn = () => {};\n\nexport default function normalizeOptions(userOptions = {}) {\n  let consoleGroup = console.group;\n  let consoleGroupEnd = console.groupEnd;\n\n  if (userOptions.collapseGroups) {\n    consoleGroup = console.groupCollapsed;\n  }\n  else if (userOptions.onlyLogs) {\n    consoleGroup = console.log;\n    consoleGroupEnd = emptyFn;\n  }\n\n  const notifier = userOptions.notifier || (\n    createDefaultNotifier(\n      ('hotReloadBufferMs' in userOptions) ? userOptions.hotReloadBufferMs : 500\n    )\n  );\n\n  return {\n    include: null,\n    exclude: null,\n    notifier,\n    onlyLogs: false,\n    consoleLog: console.log,\n    consoleGroup,\n    consoleGroupEnd,\n    logOnDifferentValues: false,\n    logOwnerReasons: true,\n    trackHooks: true,\n    titleColor: '#058',\n    diffNameColor: 'blue',\n    diffPathColor: 'red',\n    textBackgroundColor: 'white',\n    trackExtraHooks: [],\n    trackAllPureComponents: false,\n    ...userOptions,\n  };\n}\n"
  },
  {
    "path": "src/patches/patchClassComponent.js",
    "content": "import {defaults} from 'lodash';\n\nimport wdyrStore from '../wdyrStore';\n\nimport {checkIfInsideAStrictModeTree} from '../utils';\nimport getUpdateInfo from '../getUpdateInfo';\n\nexport default function patchClassComponent(ClassComponent, {displayName, defaultProps}) {\n  class WDYRPatchedClassComponent extends ClassComponent {\n    constructor(props, context) {\n      super(props, context);\n\n      this._WDYR = {\n        renderNumber: 0,\n      };\n\n      const origRender = super.render || this.render;\n\n      // this probably means that render is an arrow function or this.render.bind(this) was called on the original class\n      const renderIsABindedFunction = origRender !== ClassComponent.prototype.render;\n      if (renderIsABindedFunction) {\n        this.render = () => {\n          WDYRPatchedClassComponent.prototype.render.apply(this);\n          return origRender();\n        };\n      }\n    }\n    render() {\n      this._WDYR.renderNumber++;\n\n      if (!('isStrictMode' in this._WDYR)) {\n        this._WDYR.isStrictMode = checkIfInsideAStrictModeTree(this);\n      }\n\n      // in strict mode- ignore every other render\n      if (!(this._WDYR.isStrictMode && this._WDYR.renderNumber % 2 === 1)) {\n        if (this._WDYR.prevProps) {\n          const updateInfo = getUpdateInfo({\n            Component: ClassComponent,\n            displayName,\n            prevOwner: this._WDYR.prevOwner,\n            prevProps: this._WDYR.prevProps,\n            prevState: this._WDYR.prevState,\n            nextOwner: wdyrStore.ownerBeforeElementCreation,\n            nextProps: this.props,\n            nextState: this.state,\n          });\n\n          wdyrStore.options.notifier(updateInfo);\n        }\n\n        this._WDYR.prevOwner = wdyrStore.ownerBeforeElementCreation;\n        this._WDYR.prevProps = this.props;\n        this._WDYR.prevState = this.state;\n      }\n\n      return super.render ? super.render() : null;\n    }\n  }\n\n  try {\n    WDYRPatchedClassComponent.displayName = displayName;\n  } catch (_e) {\n    // not crucial if displayName couldn't be set\n  }\n\n  WDYRPatchedClassComponent.defaultProps = defaultProps;\n\n  defaults(WDYRPatchedClassComponent, ClassComponent);\n\n  return WDYRPatchedClassComponent;\n}\n"
  },
  {
    "path": "src/patches/patchForwardRefComponent.js",
    "content": "import {defaults} from 'lodash';\n\nimport wdyrStore from '../wdyrStore';\n\nimport getDisplayName from '../getDisplayName';\nimport {isMemoComponent} from '../utils';\nimport patchFunctionalOrStrComponent from './patchFunctionalOrStrComponent';\n\nexport default function patchForwardRefComponent(ForwardRefComponent, {displayName, defaultProps}) {\n  const {render: InnerForwardRefComponent} = ForwardRefComponent;\n\n  const isInnerComponentMemoized = isMemoComponent(InnerForwardRefComponent);\n  const WrappedFunctionalComponent = isInnerComponentMemoized ?\n    InnerForwardRefComponent.type : InnerForwardRefComponent;\n\n  const WDYRWrappedByReactForwardRefFunctionalComponent = (\n    patchFunctionalOrStrComponent(WrappedFunctionalComponent, {isPure: isInnerComponentMemoized, displayName})\n  );\n\n  WDYRWrappedByReactForwardRefFunctionalComponent.displayName = getDisplayName(WrappedFunctionalComponent);\n  WDYRWrappedByReactForwardRefFunctionalComponent.ComponentForHooksTracking = WrappedFunctionalComponent;\n  defaults(WDYRWrappedByReactForwardRefFunctionalComponent, WrappedFunctionalComponent);\n\n  const WDYRForwardRefFunctionalComponent = wdyrStore.React.forwardRef(\n    isInnerComponentMemoized ?\n      wdyrStore.React.memo(WDYRWrappedByReactForwardRefFunctionalComponent, InnerForwardRefComponent.compare) :\n      WDYRWrappedByReactForwardRefFunctionalComponent\n  );\n\n  try {\n    WDYRForwardRefFunctionalComponent.displayName = displayName;\n  } catch (_e) {\n    // not crucial if displayName couldn't be set\n  }\n\n  WDYRForwardRefFunctionalComponent.defaultProps = defaultProps;\n\n  defaults(WDYRForwardRefFunctionalComponent, ForwardRefComponent);\n\n  return WDYRForwardRefFunctionalComponent;\n}\n"
  },
  {
    "path": "src/patches/patchFunctionalOrStrComponent.js",
    "content": "import {defaults} from 'lodash';\n\nimport wdyrStore from '../wdyrStore';\n\nimport getUpdateInfo from '../getUpdateInfo';\n\nconst getFunctionalComponentFromStringComponent = (componentTypeStr) => props => (\n  wdyrStore.React.createElement(componentTypeStr, props)\n);\n\nexport default function patchFunctionalOrStrComponent(FunctionalOrStringComponent, {isPure, displayName, defaultProps}) {\n  const FunctionalComponent = typeof(FunctionalOrStringComponent) === 'string' ?\n    getFunctionalComponentFromStringComponent(FunctionalOrStringComponent) :\n    FunctionalOrStringComponent;\n\n  function WDYRFunctionalComponent(nextProps, refMaybe, ...args) {\n    const prevPropsRef = wdyrStore.React.useRef();\n    const prevProps = prevPropsRef.current;\n    prevPropsRef.current = nextProps;\n\n    const prevOwnerRef = wdyrStore.React.useRef();\n    const prevOwner = prevOwnerRef.current;\n    const nextOwner = wdyrStore.ownerBeforeElementCreation;\n    prevOwnerRef.current = nextOwner;\n\n    if (prevProps) {\n      const updateInfo = getUpdateInfo({\n        Component: FunctionalComponent,\n        displayName,\n        prevOwner,\n        nextOwner,\n        prevProps,\n        nextProps,\n      });\n\n      const notifiedByHooks = (\n        !updateInfo.reason.propsDifferences || (\n          (isPure && updateInfo.reason.propsDifferences.length === 0)\n        )\n      );\n\n      if (!notifiedByHooks) {\n        wdyrStore.options.notifier(updateInfo);\n      }\n    }\n\n    return FunctionalComponent(nextProps, refMaybe, ...args);\n  }\n\n  try {\n    WDYRFunctionalComponent.displayName = displayName;\n  } catch (_e) {\n    // not crucial if displayName couldn't be set\n  }\n\n  WDYRFunctionalComponent.defaultProps = defaultProps;\n\n  WDYRFunctionalComponent.ComponentForHooksTracking = FunctionalComponent;\n  defaults(WDYRFunctionalComponent, FunctionalComponent);\n\n  return WDYRFunctionalComponent;\n}\n"
  },
  {
    "path": "src/patches/patchMemoComponent.js",
    "content": "import {defaults} from 'lodash';\n\nimport wdyrStore from '../wdyrStore';\n\nimport getDisplayName from '../getDisplayName';\nimport {isForwardRefComponent, isMemoComponent, isReactClassComponent} from '../utils';\nimport patchClassComponent from './patchClassComponent';\nimport patchFunctionalOrStrComponent from './patchFunctionalOrStrComponent';\n\nexport default function patchMemoComponent(MemoComponent, {displayName, defaultProps}) {\n  const {type: InnerMemoComponent} = MemoComponent;\n\n  const isInnerMemoComponentAClassComponent = isReactClassComponent(InnerMemoComponent);\n  const isInnerMemoComponentForwardRefs = isForwardRefComponent(InnerMemoComponent);\n  const isInnerMemoComponentAnotherMemoComponent = isMemoComponent(InnerMemoComponent);\n\n  const WrappedFunctionalComponent = isInnerMemoComponentForwardRefs ?\n    InnerMemoComponent.render :\n    InnerMemoComponent;\n\n  const PatchedInnerComponent = isInnerMemoComponentAClassComponent ?\n    patchClassComponent(WrappedFunctionalComponent, {displayName, defaultProps}) :\n    (isInnerMemoComponentAnotherMemoComponent ?\n      patchMemoComponent(WrappedFunctionalComponent, {displayName, defaultProps}) :\n      patchFunctionalOrStrComponent(WrappedFunctionalComponent, {displayName, isPure: true})\n    );\n\n  try {\n    PatchedInnerComponent.displayName = getDisplayName(WrappedFunctionalComponent);\n  } catch (_e) {\n    // not crucial if displayName couldn't be set\n  }\n\n  PatchedInnerComponent.ComponentForHooksTracking = MemoComponent;\n  defaults(PatchedInnerComponent, WrappedFunctionalComponent);\n\n  const WDYRMemoizedFunctionalComponent = wdyrStore.React.memo(\n    isInnerMemoComponentForwardRefs ? wdyrStore.React.forwardRef(PatchedInnerComponent) : PatchedInnerComponent,\n    MemoComponent.compare\n  );\n\n  try {\n    WDYRMemoizedFunctionalComponent.displayName = displayName;\n  } catch (_e) {\n    // not crucial if displayName couldn't be set\n  }\n\n  WDYRMemoizedFunctionalComponent.defaultProps = defaultProps;\n\n  defaults(WDYRMemoizedFunctionalComponent, MemoComponent);\n\n  return WDYRMemoizedFunctionalComponent;\n}\n"
  },
  {
    "path": "src/printDiff.js",
    "content": "import {sortBy, groupBy} from 'lodash';\n\nimport calculateDeepEqualDiffs from './calculateDeepEqualDiffs';\nimport {diffTypesDescriptions} from './consts';\n\nexport default function printDiff(value1, value2, {pathString, consoleLog}) {\n  const diffs = calculateDeepEqualDiffs(value1, value2, pathString, {detailed: true});\n\n  const keysLength = Math.max(...diffs.map(diff => diff.pathString.length)) + 2;\n\n  Object.entries(groupBy(sortBy(diffs, 'pathString'), 'diffType'))\n    .forEach(([diffType, diffs]) => {\n      consoleLog(`%c${diffTypesDescriptions[diffType]}:`, 'text-decoration: underline; color: blue;');\n      diffs.forEach(diff => {\n        consoleLog(`${diff.pathString}:`.padEnd(keysLength, ' '), diff.prevValue);\n      });\n    });\n}\n"
  },
  {
    "path": "src/shouldTrack.js",
    "content": "import wdyrStore from './wdyrStore';\n\nimport {isMemoComponent} from './utils';\nimport getDisplayName from './getDisplayName';\n\nfunction shouldInclude(displayName) {\n  return (\n    wdyrStore.options.include &&\n    wdyrStore.options.include.length > 0 &&\n    wdyrStore.options.include.some(regex => regex.test(displayName))\n  );\n}\n\nfunction shouldExclude(displayName) {\n  return (\n    wdyrStore.options.exclude &&\n    wdyrStore.options.exclude.length > 0 &&\n    wdyrStore.options.exclude.some(regex => regex.test(displayName))\n  );\n}\n\nexport default function shouldTrack(Component, {isHookChange}) {\n  const displayName = getDisplayName(Component);\n\n  if (shouldExclude(displayName)) {\n    return false;\n  }\n\n  if (Component.whyDidYouRender === false) {\n    return false;\n  }\n\n  if (isHookChange && (\n    Component.whyDidYouRender && Component.whyDidYouRender.trackHooks === false\n  )) {\n    return false;\n  }\n\n  return !!(\n    Component.whyDidYouRender || (\n      wdyrStore.options.trackAllPureComponents && (\n        (Component && Component.prototype instanceof wdyrStore.React.PureComponent) ||\n        isMemoComponent(Component)\n      )\n    ) ||\n    shouldInclude(displayName)\n  );\n}\n"
  },
  {
    "path": "src/utils.js",
    "content": "// copied from https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactTypeOfMode.js\nimport {REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE, REACT_STRICT_MODE} from './consts';\n\n// based on \"findStrictRoot\" from https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactStrictModeWarnings.js\n// notice: this is only used for class components. functional components doesn't render twice inside strict mode\nexport function checkIfInsideAStrictModeTree(reactComponentInstance) {\n  let reactInternalFiber = reactComponentInstance && (\n    reactComponentInstance._reactInternalFiber ||\n    reactComponentInstance._reactInternals\n  );\n\n  while (reactInternalFiber) {\n    if (reactInternalFiber.mode & REACT_STRICT_MODE) {\n      return true;\n    }\n    reactInternalFiber = reactInternalFiber.return;\n  }\n  return false;\n}\n\nexport function isReactClassComponent(Component) {\n  return Component.prototype && !!Component.prototype.isReactComponent;\n}\n\nexport function isMemoComponent(Component) {\n  return Component.$$typeof === REACT_MEMO_TYPE;\n}\n\nexport function isForwardRefComponent(Component) {\n  return Component.$$typeof === REACT_FORWARD_REF_TYPE;\n}\n"
  },
  {
    "path": "src/wdyrStore.js",
    "content": "const wdyrStore = {\n  /* The React object we patch */\n  React: undefined,\n\n  /* Processed user options for WDYR */\n  options: undefined,\n\n  /* The original React.createElement function */\n  origCreateElement: undefined,\n\n  /* The original React.createFactory function */\n  origCreateFactory: undefined,\n\n  /* The original React.cloneElement function */\n  origCloneElement: undefined,\n\n  /* A weak map of all React elements to their WDYR patched react elements */\n  componentsMap: new WeakMap(),\n\n  /* A weak map of props to the owner element that passed them */\n  ownerDataMap: new WeakMap(),\n\n  /* An array of infos for hooks tracked during current render */\n  hooksInfoForCurrentRender: new WeakMap(),\n\n  /* Owner before element creation started */\n  ownerBeforeElementCreation: null,\n};\n\nexport default wdyrStore;\n"
  },
  {
    "path": "src/whyDidYouRender.js",
    "content": "import {get, isFunction} from 'lodash';\n\nimport wdyrStore from './wdyrStore';\n\nimport normalizeOptions from './normalizeOptions';\nimport getDisplayName from './getDisplayName';\nimport getDefaultProps from './getDefaultProps';\nimport getUpdateInfo from './getUpdateInfo';\nimport shouldTrack from './shouldTrack';\n\nimport patchClassComponent from './patches/patchClassComponent';\nimport patchFunctionalOrStrComponent from './patches/patchFunctionalOrStrComponent';\nimport patchMemoComponent from './patches/patchMemoComponent';\nimport patchForwardRefComponent from './patches/patchForwardRefComponent';\n\nimport {\n  isForwardRefComponent,\n  isMemoComponent,\n  isReactClassComponent,\n} from './utils';\n\nimport {dependenciesMap} from './calculateDeepEqualDiffs';\n\nimport {getCurrentOwner} from './helpers';\n\nexport {wdyrStore, getCurrentOwner};\n\nconst initialHookValue = Symbol('initial-hook-value');\n\nfunction trackHookChanges(hookName, {path: pathToGetTrackedHookResult}, rawHookResult) {\n  const nextResult = pathToGetTrackedHookResult ? get(rawHookResult, pathToGetTrackedHookResult) : rawHookResult;\n\n  const prevResultRef = wdyrStore.React.useRef(initialHookValue);\n  const prevResult = prevResultRef.current;\n  prevResultRef.current = nextResult;\n\n  const ownerInstance = getCurrentOwner();\n  if (!ownerInstance) {\n    return rawHookResult;\n  }\n\n  if (!wdyrStore.hooksInfoForCurrentRender.has(ownerInstance)) {\n    wdyrStore.hooksInfoForCurrentRender.set(ownerInstance, []);\n  }\n  const hooksInfoForCurrentRender = wdyrStore.hooksInfoForCurrentRender.get(ownerInstance);\n\n  hooksInfoForCurrentRender.push({hookName, result: nextResult});\n\n  const Component = ownerInstance.type.ComponentForHooksTracking || ownerInstance.type;\n  const displayName = getDisplayName(Component);\n\n  const isShouldTrack = shouldTrack(Component, {isHookChange: true});\n  if (isShouldTrack && prevResult !== initialHookValue) {\n    const updateInfo = getUpdateInfo({\n      Component: Component,\n      displayName,\n      hookName,\n      prevHookResult: prevResult,\n      nextHookResult: nextResult,\n    });\n \n    if (updateInfo.reason.hookDifferences) {\n      wdyrStore.options.notifier(updateInfo);\n    }\n  }\n\n  return rawHookResult;\n}\n\nfunction createPatchedComponent(Component, {displayName, defaultProps}) {\n  if (isMemoComponent(Component)) {\n    return patchMemoComponent(Component, {displayName, defaultProps});\n  }\n\n  if (isForwardRefComponent(Component)) {\n    return patchForwardRefComponent(Component, {displayName, defaultProps});\n  }\n\n  if (isReactClassComponent(Component)) {\n    return patchClassComponent(Component, {displayName, defaultProps});\n  }\n\n  return patchFunctionalOrStrComponent(Component, {displayName, defaultProps, isPure: false});\n}\n\nfunction getPatchedComponent(Component, {displayName, defaultProps}) {\n  if (wdyrStore.componentsMap.has(Component)) {\n    return wdyrStore.componentsMap.get(Component);\n  }\n\n  const WDYRPatchedComponent = createPatchedComponent(Component, {displayName, defaultProps});\n\n  wdyrStore.componentsMap.set(Component, WDYRPatchedComponent);\n\n  return WDYRPatchedComponent;\n}\n\nfunction getIsSupportedComponentType(Comp) {\n  if (!Comp) {\n    return false;\n  }\n\n  if (isMemoComponent(Comp)) {\n    return getIsSupportedComponentType(Comp.type);\n  }\n\n  if (isForwardRefComponent(Comp)) {\n    return getIsSupportedComponentType(Comp.render);\n  }\n\n  if (typeof Comp === 'function') {\n    return true;\n  }\n}\n\nexport const hooksConfig = {\n  useState: {path: '0'},\n  useReducer: {path: '0'},\n  useContext: undefined,\n  useSyncExternalStore: undefined,\n  useMemo: {dependenciesPath: '1', dontReport: true},\n  useCallback: {dependenciesPath: '1', dontReport: true},\n};\n\nexport function storeOwnerData(element) {\n  const owner = getCurrentOwner();\n  if (owner) {\n    const Component = owner.type.ComponentForHooksTracking || owner.type;\n    const displayName = getDisplayName(Component);\n\n    let additionalOwnerData = {};\n    if (wdyrStore.options.getAdditionalOwnerData) {\n      additionalOwnerData = wdyrStore.options.getAdditionalOwnerData(element);\n    }\n\n    wdyrStore.ownerDataMap.set(owner, {\n      Component,\n      displayName,\n      props: owner.pendingProps,\n      state: owner.stateNode ? owner.stateNode.state : null,\n      hooksInfo: wdyrStore.hooksInfoForCurrentRender.get(owner) || [],\n      additionalOwnerData,\n    });\n\n    wdyrStore.hooksInfoForCurrentRender.delete(owner);\n  }\n}\n\nfunction trackHooksIfNeeded() {\n  const hooksSupported = !!wdyrStore.React.useState;\n\n  if (wdyrStore.options.trackHooks && hooksSupported) {\n    const nativeHooks = Object.entries(hooksConfig).map(([hookName, hookTrackingConfig]) => {\n      return [wdyrStore.React, hookName, hookTrackingConfig];\n    });\n\n    const hooksToTrack = [\n      ...nativeHooks,\n      ...wdyrStore.options.trackExtraHooks,\n    ];\n\n    hooksToTrack.forEach(([hookParent, hookName, hookTrackingConfig = {}]) => {\n      const originalHook = hookParent[hookName];\n\n      const newHook = function useWhyDidYouRenderReWrittenHook(...args) {\n        const hookResult = originalHook.call(this, ...args);\n        const {dependenciesPath, dontReport} = hookTrackingConfig;\n        const shouldTrackHookChanges = !dontReport;\n        if (dependenciesPath && isFunction(hookResult)) {\n          dependenciesMap.set(hookResult, {hookName, deps: get(args, dependenciesPath)});\n        }\n        if (shouldTrackHookChanges) {\n          trackHookChanges(hookName, hookTrackingConfig, hookResult);\n        }\n        return hookResult;\n      };\n\n      Object.defineProperty(newHook, 'name', {\n        value: hookName + 'WDYR',\n        writable: false\n      });\n      Object.assign(newHook, {originalHook});\n      hookParent[hookName] = newHook;\n    });\n  }\n}\n\nexport function getWDYRType(origType) {\n  const isShouldTrack = (\n    getIsSupportedComponentType(origType) &&\n    shouldTrack(origType, {isHookChange: false})\n  );\n\n  if (!isShouldTrack) {\n    return null;\n  }\n\n  const displayName = (\n    origType &&\n    origType.whyDidYouRender &&\n    origType.whyDidYouRender.customName ||\n    getDisplayName(origType)\n  );\n\n  const defaultProps = getDefaultProps(origType);\n\n  const WDYRPatchedComponent = getPatchedComponent(origType, {displayName, defaultProps});\n\n  return WDYRPatchedComponent;\n}\n\nexport default function whyDidYouRender(React, userOptions) {\n  if (React.__IS_WDYR__) {\n    return;\n  }\n  React.__IS_WDYR__ = true;\n\n  Object.assign(wdyrStore, {\n    React,\n    options: normalizeOptions(userOptions),\n    origCreateElement: React.createElement,\n    origCreateFactory: React.createFactory,\n    origCloneElement: React.cloneElement,\n    componentsMap: new WeakMap(),\n  });\n\n  React.createElement = function(origType, ...rest) {\n    const WDYRType = getWDYRType(origType);\n    if (WDYRType) {\n      try {\n        wdyrStore.ownerBeforeElementCreation = getCurrentOwner();\n        const element = wdyrStore.origCreateElement.apply(React, [WDYRType, ...rest]);\n        if (wdyrStore.options.logOwnerReasons) {\n          storeOwnerData(element);\n        }\n        return element;\n      }\n      catch (e) {\n        wdyrStore.options.consoleLog('whyDidYouRender error in createElement. Please file a bug at https://github.com/welldone-software/why-did-you-render/issues.', {\n          errorInfo: {\n            error: e,\n            componentNameOrComponent: origType,\n            rest,\n            options: wdyrStore.options,\n          },\n        });\n      }\n    }\n\n    return wdyrStore.origCreateElement.apply(React, [origType, ...rest]);\n  };\n  Object.assign(React.createElement, wdyrStore.origCreateElement);\n\n  React.createFactory = type => {\n    const factory = React.createElement.bind(null, type);\n    factory.type = type;\n    return factory;\n  };\n  Object.assign(React.createFactory, wdyrStore.origCreateFactory);\n\n  React.cloneElement = (...args) => {\n    wdyrStore.ownerBeforeElementCreation = getCurrentOwner();\n    const element = wdyrStore.origCloneElement.apply(React, args);\n    if (wdyrStore.options.logOwnerReasons) {\n      storeOwnerData(element);\n    }\n\n    return element;\n  };\n  Object.assign(React.cloneElement, wdyrStore.origCloneElement);\n\n  trackHooksIfNeeded();\n\n  React.__REVERT_WHY_DID_YOU_RENDER__ = () => {\n    Object.assign(React, {\n      createElement: wdyrStore.origCreateElement,\n      createFactory: wdyrStore.origCreateFactory,\n      cloneElement: wdyrStore.origCloneElement,\n    });\n\n    wdyrStore.componentsMap = null;\n\n    const hooksToRevert = [\n      ...Object.keys(hooksConfig).map(hookName => [React, hookName]),\n      ...wdyrStore.options.trackExtraHooks,\n    ];\n    hooksToRevert.forEach(([hookParent, hookName]) => {\n      if (hookParent[hookName].originalHook) {\n        hookParent[hookName] = hookParent[hookName].originalHook;\n      }\n    });\n\n    delete React.__REVERT_WHY_DID_YOU_RENDER__;\n    delete React.__IS_WDYR__;\n  };\n\n  return React;\n}\n"
  },
  {
    "path": "tests/.eslintrc",
    "content": "{\n  \"extends\": [\n    \"plugin:jest/recommended\",\n    \"../.eslintrc\"\n  ],\n  \"rules\": {\n    \"jest/expect-expect\": \"off\",\n    \"jest/valid-title\": \"off\"\n  }\n}\n"
  },
  {
    "path": "tests/babel.config.cjs",
    "content": "module.exports = require('../babel.config');\n"
  },
  {
    "path": "tests/calculateDeepEqualDiffs.test.js",
    "content": "import React from 'react';\n\nimport calculateDeepEqualDiffs from '~/calculateDeepEqualDiffs';\nimport {diffTypes} from '~/consts';\n\ntest('same', () => {\n  const prevValue = {a: 'b'};\n  const nextValue = prevValue;\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([]);\n});\n\ntest('not deep equal', () => {\n  const prevValue = {a: 'b'};\n  const nextValue = {a: 'c'};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '.a',\n      prevValue: 'b',\n      nextValue: 'c',\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('simple deep', () => {\n  const prevValue = {a: 'b'};\n  const nextValue = {a: 'b'};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('nested object deep equals', () => {\n  const prevValue = {a: {b: 'c'}};\n  const nextValue = {a: {b: 'c'}};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('nested array deep equals', () => {\n  const prevValue = {a: {b: ['c']}};\n  const nextValue = {a: {b: ['c']}};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('date', () => {\n  const now = new Date();\n  const now2 = new Date(now);\n\n  const diffs = calculateDeepEqualDiffs(now, now2);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue: now,\n      nextValue: now2,\n      diffType: diffTypes.date,\n    },\n  ]);\n});\n\ntest('nested date', () => {\n  const now = new Date();\n  const now2 = new Date(now);\n\n  const prevValue = {a: {b: [now]}};\n  const nextValue = {a: {b: [now2]}};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('regular expression', () => {\n  const regEx = /c/i;\n  const regEx2 = /c/i;\n\n  const diffs = calculateDeepEqualDiffs(regEx, regEx2);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue: regEx,\n      nextValue: regEx2,\n      diffType: diffTypes.regex,\n    },\n  ]);\n});\n\ntest('nested regular expression', () => {\n  const regEx = /c/i;\n  const regEx2 = /c/i;\n\n  const prevValue = {a: {b: [regEx]}};\n  const nextValue = {a: {b: [regEx2]}};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('dom elements', () => {\n  const element = document.createElement('div');\n  const element2 = document.createElement('div');\n\n  const prevValue = {a: element};\n  const nextValue = {a: element2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '.a',\n      prevValue: prevValue.a,\n      nextValue: nextValue.a,\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('equal react elements', () => {\n  const tooltip = <div>hi!</div>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('simple react elements', () => {\n  const tooltip = <div>hi!</div>;\n  const tooltip2 = <div>hi!</div>;\n\n  const diffs = calculateDeepEqualDiffs(tooltip, tooltip2);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue: tooltip,\n      nextValue: tooltip2,\n      diffType: diffTypes.reactElement,\n    },\n  ]);\n});\n\ntest('nested react elements', () => {\n  const tooltip = <div>hi!</div>;\n  const tooltip2 = <div>hi!</div>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('nested different react elements', () => {\n  const tooltip = <div>hi!</div>;\n  const tooltip2 = <div>hi 2 !</div>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '.a',\n      prevValue: tooltip,\n      nextValue: tooltip2,\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('nested different react elements with several children', () => {\n  const prevValue = <div><a>hi</a><a>hi111</a></div>;\n  const nextValue = <div><a>hi</a><a>hi222</a></div>;\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('nested different react elements with several children with keys', () => {\n  const prevValue = <div><a key=\"a\">hi</a><a key=\"b\">hi111</a></div>;\n  const nextValue = <div><a key=\"a\">hi</a><a key=\"b\">hi222</a></div>;\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('react class component instance', () => {\n  class MyComponent extends React.Component {\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const tooltip = <MyComponent/>;\n  const tooltip2 = <MyComponent/>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('react class pure component instance', () => {\n  class MyComponent extends React.PureComponent {\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const tooltip = <MyComponent/>;\n  const tooltip2 = <MyComponent/>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('react functional component instance', () => {\n  const MyFunctionalComponent = () => (\n    <div>hi!</div>\n  );\n\n  const tooltip = <MyFunctionalComponent/>;\n  const tooltip2 = <MyFunctionalComponent/>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('react memoized functional component instance', () => {\n  const MyFunctionalComponent = React.memo(() => (\n    <div>hi!</div>\n  ));\n\n  const tooltip = <MyFunctionalComponent a={1}/>;\n  const tooltip2 = <MyFunctionalComponent a={1}/>;\n\n  const prevValue = {a: tooltip};\n  const nextValue = {a: tooltip2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('functions', () => {\n  const fn = function something() {};\n  const fn2 = function something() {};\n\n  const prevValue = {fn};\n  const nextValue = {fn: fn2};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('inline functions', () => {\n  const prevValue = {a: {fn: () => {}}};\n  const nextValue = {a: {fn: () => {}}};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('sets', () => {\n  const prevValue = {\n    a: new Set(['a']),\n    b: new Set(['a', 1]),\n    c: new Set(['a', 1]),\n  };\n\n  const nextValue = {\n    a: new Set(['a']),\n    b: new Set(['a', 2]),\n    c: new Set(['a', 1, 'c']),\n  };\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n  expect(diffs).toEqual([\n    {\n      pathString: '.c',\n      prevValue: prevValue.c,\n      nextValue: nextValue.c,\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '.b',\n      prevValue: prevValue.b,\n      nextValue: nextValue.b,\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '.a',\n      prevValue: prevValue.a,\n      nextValue: nextValue.a,\n      diffType: diffTypes.deepEquals,\n    },\n    {\n      pathString: '',\n      prevValue: prevValue,\n      nextValue: nextValue,\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n\ntest('mix', () => {\n  const prevValue = {a: {fn: () => {}}, b: [{tooltip: <div>hi</div>}]};\n  const nextValue = {a: {fn: () => {}}, b: [{tooltip: <div>hi</div>}]};\n\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\ndescribe('calculateDeepEqualDiffs - Errors', () => {\n  test('Equal Native Errors', () => {\n    const prevValue = new Error('message');\n    const nextValue = new Error('message');\n    const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        prevValue,\n        nextValue,\n        diffType: diffTypes.deepEquals,\n      },\n    ]);\n  });\n  \n  test('Different Native Errors', () => {\n    const prevValue = new Error('message');\n    const nextValue = new Error('Second message');\n    const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n    expect(diffs).toEqual([\n      {\n        pathString: '.message',\n        prevValue: 'message',\n        nextValue: 'Second message',\n        diffType: diffTypes.different,\n      },\n      {\n        pathString: '',\n        prevValue,\n        nextValue,\n        diffType: diffTypes.different,\n      },\n    ]);\n  });\n\n  test('Equal Custom Errors', () => {\n    class CustomError extends Error {\n      constructor(message, code) {\n        super(message);\n        this.name = 'ValidationError';\n        this.code = code;\n      }\n    }\n\n    const prevValue = new CustomError('message', 1001);\n    const nextValue = new CustomError('message', 1001);\n    const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        prevValue,\n        nextValue,\n        diffType: diffTypes.deepEquals,\n      },\n    ]);\n  });\n\n  test('Different Custom Errors', () => {\n    class CustomError extends Error {\n      constructor(message, code) {\n        super(message);\n        this.name = 'ValidationError';\n        this.code = code;\n      }\n    }\n\n    const prevValue = new CustomError('message', 1001);\n    const nextValue = new CustomError('message', 1002);\n    const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n    expect(diffs).toEqual([\n      {\n        pathString: '.code',\n        prevValue: 1001,\n        nextValue: 1002,\n        diffType: diffTypes.different,\n      },\n      {\n        pathString: '',\n        prevValue,\n        nextValue,\n        diffType: diffTypes.different,\n      },\n    ]);\n  });\n});\n\n\ntest('Equal class instances', () => {\n  class Person {\n    constructor(name) {\n      this.name = name;\n    }\n  }\n  \n  const prevValue = new Person('Jon Snow');\n  const nextValue = new Person('Jon Snow');\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n  expect(diffs).toEqual([\n    {\n      pathString: '',\n      prevValue,\n      nextValue,\n      diffType: diffTypes.deepEquals,\n    },\n  ]);\n});\n\ntest('Different class instances', () => {\n  class Person {\n    constructor(name) {\n      this.name = name;\n    }\n  }\n  \n  const prevValue = new Person('Jon Snow');\n  const nextValue = new Person('Aria Stark');\n  const diffs = calculateDeepEqualDiffs(prevValue, nextValue);\n  expect(diffs).toEqual([\n    {\n      pathString: '.name',\n      prevValue: 'Jon Snow',\n      nextValue: 'Aria Stark',\n      diffType: diffTypes.different,\n    },\n    {\n      pathString: '',\n      prevValue: {\n        name: 'Jon Snow',\n      },\n      nextValue: {\n        name: 'Aria Stark',\n      },\n      diffType: diffTypes.different,\n    },\n  ]);\n});\n"
  },
  {
    "path": "tests/defaultNotifier.test.js",
    "content": "import React from 'react';\n\nimport defaultNotifier from '~/defaultNotifier';\nimport getUpdateInfo from '~/getUpdateInfo';\nimport whyDidYouRender from '~';\n\nclass TestComponent extends React.Component {\n  static whyDidYouRender = true;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nconst testInputAndExpects = {\n  default: {\n    description: 'Group by component (default options)',\n    userOptions: undefined,\n    expects: {\n      logsCount: {\n        title: 0,\n        emptyValues: 1,\n        changedObjects: 2,\n        changedObjectValues: 3,\n        changedObjectValuesDeepEquals: 4,\n      },\n      groupLogsCount: {\n        title: 1,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 1,\n        changedObjectValuesDeepEquals: 1,\n      },\n      groupCollapsedLogsCount: {\n        title: 0,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 0,\n        changedObjectValuesDeepEquals: 0,\n      },\n    },\n  },\n  onlyLogs: {\n    description: 'Only logs',\n    userOptions: {onlyLogs: true},\n    expects: {\n      logsCount: {\n        title: 1,\n        emptyValues: 1,\n        changedObjects: 2,\n        changedObjectValues: 4,\n        changedObjectValuesDeepEquals: 5,\n      },\n      groupLogsCount: {\n        title: 0,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 0,\n        changedObjectValuesDeepEquals: 0,\n      },\n      groupCollapsedLogsCount: {\n        title: 0,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 0,\n        changedObjectValuesDeepEquals: 0,\n      },\n    },\n  },\n  collapseGroups: {\n    description: 'Group by component with collapse',\n    userOptions: {collapseGroups: true},\n    expects: {\n      logsCount: {\n        title: 0,\n        emptyValues: 1,\n        changedObjects: 2,\n        changedObjectValues: 3,\n        changedObjectValuesDeepEquals: 4,\n      },\n      groupLogsCount: {\n        title: 0,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 0,\n        changedObjectValuesDeepEquals: 0,\n      },\n      groupCollapsedLogsCount: {\n        title: 1,\n        emptyValues: 0,\n        changedObjects: 0,\n        changedObjectValues: 1,\n        changedObjectValuesDeepEquals: 1,\n      },\n    },\n  },\n};\n\nfunction calculateNumberOfExpectedLogs(expectedLogTypes, expectedCounts) {\n  return expectedLogTypes.reduce((sum, type) => sum + expectedCounts[type], 0);\n}\n\nfunction expectLogTypes(expectedLogTypes, expects) {\n  const consoleOutputs = flushConsoleOutput();\n\n  expect(consoleOutputs.filter(o => o.level === 'log'))\n    .toHaveLength(calculateNumberOfExpectedLogs(expectedLogTypes, expects.logsCount));\n\n  expect(consoleOutputs.filter(o => o.level === 'group'))\n    .toHaveLength(calculateNumberOfExpectedLogs(expectedLogTypes, expects.groupLogsCount));\n\n  expect(consoleOutputs.filter(o => o.level === 'groupCollapsed'))\n    .toHaveLength(calculateNumberOfExpectedLogs(expectedLogTypes, expects.groupCollapsedLogsCount));\n}\n\ndescribe('For no differences', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: null,\n        prevState: null,\n        nextProps: null,\n        nextState: null,\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'emptyValues'], expects);\n    });\n  });\n});\n\ndescribe('For different props eq by ref', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {a: 'aa'},\n        prevState: null,\n        nextProps: {a: 'aa'},\n        nextState: null,\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjects'], expects);\n    });\n  });\n});\n\ndescribe('For equal state eq by ref', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: null,\n        prevState: {a: 'aa'},\n        nextProps: null,\n        nextState: {a: 'aa'},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjects'], expects);\n    });\n  });\n});\n\ndescribe('For different state and props', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {a: 'aa'},\n        prevState: {a: 'aa'},\n        nextProps: {a: 'aa'},\n        nextState: {a: 'aa'},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjects', 'changedObjects'], expects);\n    });\n  });\n});\n\ndescribe('For different hook', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevHookResult: {a: 'aa'},\n        nextHookResult: {a: 'aa'},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValuesDeepEquals'], expects);\n    });\n  });\n});\n\ndescribe('For different deep equal props', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {a: {b: 'b'}},\n        prevState: null,\n        nextProps: {a: {b: 'b'}},\n        nextState: null,\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValuesDeepEquals'], expects);\n    });\n  });\n});\n\ndescribe('For different deep equal state', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: null,\n        prevState: {a: {b: 'b'}},\n        nextProps: null,\n        nextState: {a: {b: 'b'}},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValuesDeepEquals'], expects);\n    });\n  });\n});\n\ndescribe('For different deep equal state and props', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {a: {b: 'b'}},\n        prevState: {a: {b: 'b'}},\n        nextProps: {a: {b: 'b'}},\n        nextState: {a: {b: 'b'}},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValuesDeepEquals', 'changedObjectValuesDeepEquals'], expects);\n    });\n  });\n});\n\ndescribe('For different functions by the same name', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {fn: function something() {}},\n        prevState: null,\n        nextProps: {fn: function something() {}},\n        nextState: null,\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValues'], expects);\n    });\n  });\n});\n\ndescribe('Mix of changes', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  Object.values(testInputAndExpects).forEach(({description, userOptions, expects}) => {\n    test(description, () => {\n      whyDidYouRender(React, userOptions);\n\n      const updateInfo = getUpdateInfo({\n        Component: TestComponent,\n        prevProps: {fn: function something() {}},\n        prevState: {a: {b: 'b'}},\n        nextProps: {fn: function something() {}},\n        nextState: {a: {b: 'b'}},\n      });\n\n      defaultNotifier(updateInfo);\n\n      expectLogTypes(['title', 'changedObjectValues', 'changedObjectValuesDeepEquals'], expects);\n    });\n  });\n});\n\ndescribe('logOnDifferentProps option', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('For different props', () => {\n    whyDidYouRender(React, {onlyLogs: true});\n\n    const updateInfo = getUpdateInfo({\n      Component: TestComponent,\n      prevProps: {a: 'aaaa'},\n      prevState: null,\n      nextProps: {a: 'bbbb'},\n      nextState: null,\n    });\n\n    defaultNotifier(updateInfo);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toHaveLength(0);\n  });\n\n  test('For different state', () => {\n    whyDidYouRender(React, {onlyLogs: true});\n\n    const updateInfo = getUpdateInfo({\n      Component: TestComponent,\n      prevProps: null,\n      prevState: {a: 'aaaa'},\n      nextProps: null,\n      nextState: {a: 'bbbb'},\n    });\n\n    defaultNotifier(updateInfo);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toHaveLength(0);\n  });\n\n  test('For different props with logOnDifferentValues', () => {\n    whyDidYouRender(React, {logOnDifferentValues: true, onlyLogs: true});\n\n    const updateInfo = getUpdateInfo({\n      Component: TestComponent,\n      prevProps: {a: 'aaaa'},\n      prevState: null,\n      nextProps: {a: 'bbbb'},\n      nextState: null,\n    });\n\n    defaultNotifier(updateInfo);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toHaveLength(\n      calculateNumberOfExpectedLogs(\n        ['title', 'changedObjectValues'],\n        testInputAndExpects.onlyLogs.expects.logsCount\n      )\n    );\n  });\n\n  test('For different props with logOnDifferentValues for a specific component', () => {\n    whyDidYouRender(React, {onlyLogs: true});\n\n    class OwnTestComponent extends React.Component {\n      static whyDidYouRender = {logOnDifferentValues: true};\n      render() {\n        return <div>hi!</div>;\n      }\n    }\n\n    const updateInfo = getUpdateInfo({\n      Component: OwnTestComponent,\n      prevProps: {a: 'aaaa'},\n      prevState: null,\n      nextProps: {a: 'bbbb'},\n      nextState: null,\n    });\n\n    defaultNotifier(updateInfo);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toHaveLength(\n      calculateNumberOfExpectedLogs(\n        ['title', 'changedObjectValues'],\n        testInputAndExpects.onlyLogs.expects.logsCount\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "tests/findObjectsDifferences.test.js",
    "content": "import findObjectsDifferences from '~/findObjectsDifferences';\nimport {diffTypes} from '~/consts';\n\ndescribe('findObjectsDifferences shallow', () => {\n  test('for empty values', () => {\n    const prev = null;\n    const next = null;\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual(false);\n  });\n\n  test('For no differences', () => {\n    const prev = {prop: 'value'};\n    const next = prev;\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual(false);\n  });\n\n  test('For prev empty value', () => {\n    const prev = null;\n    const next = {prop: 'value'};\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([\n      {\n        pathString: 'prop',\n        diffType: diffTypes.different,\n        prevValue: undefined,\n        nextValue: 'value',\n      },\n    ]);\n  });\n\n  test('For next empty value', () => {\n    const prev = {prop: 'value'};\n    const next = null;\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([\n      {\n        pathString: 'prop',\n        diffType: diffTypes.different,\n        prevValue: 'value',\n        nextValue: undefined,\n      },\n    ]);\n  });\n\n  test('For objects different by reference but equal by value', () => {\n    const prop2 = {a: 'a'};\n    const prev = {prop: 'value', prop2};\n    const next = {prop: 'value', prop2};\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([]);\n  });\n\n  test('For props inside the object different by reference but equal by value', () => {\n    const prev = {prop: {a: 'a'}};\n    const next = {prop: {a: 'a'}};\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([\n      {\n        pathString: 'prop',\n        diffType: diffTypes.deepEquals,\n        prevValue: prev.prop,\n        nextValue: next.prop,\n      },\n    ]);\n  });\n\n  test('For functions inside the object with the same name', () => {\n    const prev = {fn: function something() {}};\n    const next = {fn: function something() {}};\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([\n      {\n        pathString: 'fn',\n        diffType: diffTypes.function,\n        prevValue: prev.fn,\n        nextValue: next.fn,\n      },\n    ]);\n  });\n\n  test('Mix of differences inside the objects', () => {\n    const prev = {prop: 'value', prop2: {a: 'a'}, prop3: 'AA', fn: function something() {}};\n    const next = {prop: 'value', prop2: {a: 'a'}, prop3: 'ZZ', fn: function something() {}};\n    const diffs = findObjectsDifferences(prev, next);\n    expect(diffs).toEqual([\n      {\n        pathString: 'prop2',\n        diffType: diffTypes.deepEquals,\n        prevValue: prev.prop2,\n        nextValue: next.prop2,\n      },\n      {\n        pathString: 'prop3',\n        diffType: diffTypes.different,\n        prevValue: prev.prop3,\n        nextValue: next.prop3,\n      },\n      {\n        pathString: 'fn',\n        diffType: diffTypes.function,\n        prevValue: prev.fn,\n        nextValue: next.fn,\n      },\n    ]);\n  });\n});\n\ndescribe('findObjectsDifferences not shallow', () => {\n  test('for empty values', () => {\n    const prev = null;\n    const next = null;\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual(false);\n  });\n\n  test('For no differences', () => {\n    const prev = {prop: 'value'};\n    const next = prev;\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual(false);\n  });\n\n  test('For prev empty value', () => {\n    const prev = null;\n    const next = {prop: 'value'};\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        diffType: diffTypes.different,\n        prevValue: null,\n        nextValue: {prop: 'value'},\n      },\n    ]);\n  });\n\n  test('For next empty value', () => {\n    const prev = {prop: 'value'};\n    const next = null;\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        diffType: diffTypes.different,\n        prevValue: {prop: 'value'},\n        nextValue: null,\n      },\n    ]);\n  });\n\n  test('For objects different by reference but equal by value', () => {\n    const prop2 = {a: 'a'};\n    const prev = {prop: 'value', prop2};\n    const next = {prop: 'value', prop2};\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        diffType: diffTypes.deepEquals,\n        prevValue: {prop: 'value', prop2},\n        nextValue: {prop: 'value', prop2},\n      },\n    ]);\n  });\n\n  test('For sets with same values', () => {\n    const prev = new Set([1, 2, 3]);\n    const next = new Set([1, 2, 3]);\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([{\n      pathString: '',\n      diffType: diffTypes.deepEquals,\n      prevValue: prev,\n      nextValue: next,\n    }]);\n  });\n\n  test('For sets with different values', () => {\n    const prev = new Set([1, 2, 3]);\n    const next = new Set([4, 5, 6]);\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        diffType: diffTypes.different,\n        prevValue: prev,\n        nextValue: next,\n      },\n    ]);\n  });\n\n  test('For sets with different value length', () => {\n    const prev = new Set([1, 2, 3]);\n    const next = new Set([1, 2, 3, 4]);\n    const diffs = findObjectsDifferences(prev, next, {shallow: false});\n    expect(diffs).toEqual([\n      {\n        pathString: '',\n        diffType: diffTypes.different,\n        prevValue: prev,\n        nextValue: next,\n      },\n    ]);\n  });\n});\n"
  },
  {
    "path": "tests/getDisplayName.test.js",
    "content": "import React from 'react';\n\nimport getDisplayName from '~/getDisplayName';\n\ntest('For a component', () => {\n  class TestComponent extends React.Component {\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n  const displayName = getDisplayName(TestComponent);\n  expect(displayName).toBe('TestComponent');\n});\n\ntest('For inline functions', () => {\n  const InlineComponent = () => (\n    <div>hi!</div>\n  );\n  InlineComponent.displayName = 'InlineComponentCustomName';\n  const displayName = getDisplayName(InlineComponent);\n  expect(displayName).toBe('InlineComponentCustomName');\n});\n\ntest('For inline functions with no name', () => {\n  const InlineComponent = () => (\n    <div>hi!</div>\n  );\n  const displayName = getDisplayName(InlineComponent);\n  expect(displayName).toBe('InlineComponent');\n});\n"
  },
  {
    "path": "tests/getUpdateInfo.test.js",
    "content": "import React from 'react';\n\nimport {diffTypes} from '~/consts';\nimport getUpdateInfo from '~/getUpdateInfo';\nimport getDisplayName from '~/getDisplayName';\nimport whyDidYouRender from '~';\n\nclass TestComponent extends React.Component {\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\ndescribe('getUpdateInfo', () => {\n  beforeEach(() => {\n    whyDidYouRender(React);\n  });\n\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('Empty props and state', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {},\n      prevState: null,\n      nextProps: {},\n      nextState: null,\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      ownerDataMap: expect.any(WeakMap),\n      displayName: 'TestComponent',\n      reason: {\n        propsDifferences: [],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Same props', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: 1},\n      prevState: null,\n      nextProps: {a: 1},\n      nextState: null,\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      ownerDataMap: expect.any(WeakMap),\n      displayName: 'TestComponent',\n      reason: {\n        propsDifferences: [],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Same state', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {},\n      prevState: {a: 1},\n      nextProps: {},\n      nextState: {a: 1},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      ownerDataMap: expect.any(WeakMap),\n      displayName: 'TestComponent',\n      reason: {\n        propsDifferences: [],\n        stateDifferences: [],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Same props and state', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {b: 1},\n      prevState: {a: 1},\n      nextProps: {b: 1},\n      nextState: {a: 1},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      ownerDataMap: expect.any(WeakMap),\n      displayName: 'TestComponent',\n      reason: {\n        propsDifferences: [],\n        stateDifferences: [],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props change', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: 1},\n      prevState: null,\n      nextProps: {a: 2},\n      nextState: null,\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.different,\n            prevValue: input.prevProps.a,\n            nextValue: input.nextProps.a,\n          },\n        ],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('State change', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {},\n      prevState: {a: 1},\n      nextProps: {},\n      nextState: {a: 2},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.different,\n            prevValue: input.prevState.a,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props and state change', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {b: 1},\n      prevState: {a: 1},\n      nextProps: {b: 2},\n      nextState: {a: 2},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'b',\n            diffType: diffTypes.different,\n            prevValue: input.prevProps.b,\n            nextValue: input.nextProps.b,\n          },\n        ],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.different,\n            prevValue: input.prevState.a,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props change by ref', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: {b: 'b'}},\n      prevState: null,\n      nextProps: {a: {b: 'b'}},\n      nextState: null,\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevProps.a,\n            nextValue: input.nextProps.a,\n          },\n        ],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('State changed by ref', () => {\n\n\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {},\n      prevState: {a: {b: 'b'}},\n      nextProps: {},\n      nextState: {a: {b: 'b'}},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevState.a,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props and state different by ref', () => {\n\n\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {b: {c: 'c'}},\n      prevState: {a: {d: 'd'}},\n      nextProps: {b: {c: 'c'}},\n      nextState: {a: {d: 'd'}},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'b',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevProps.b,\n            nextValue: input.nextProps.b,\n          },\n        ],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevState.a,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props change by function', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: () => {}},\n      prevState: null,\n      nextProps: {a: () => {}},\n      nextState: null,\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.function,\n            prevValue: input.prevProps.a,\n            nextValue: input.nextProps.a,\n          },\n        ],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('State changed by function ref', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {},\n      prevState: {a: () => {}},\n      nextProps: {},\n      nextState: {a: () => {}},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.function,\n            prevValue: input.prevState.a,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Props and state different by function', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: () => {}},\n      prevState: {b: () => {}},\n      nextProps: {a: () => {}},\n      nextState: {b: () => {}},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.function,\n            prevValue: input.prevProps.a,\n            nextValue: input.nextProps.a,\n          },\n        ],\n        stateDifferences: [\n          {\n            pathString: 'b',\n            diffType: diffTypes.function,\n            prevValue: input.prevState.b,\n            nextValue: input.nextState.b,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('Mix of differences', () => {\n    const input = {\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps: {a: () => {}, b: '123', c: {d: 'e'}, f: 3},\n      prevState: null,\n      nextProps: {a: () => {}, b: '12345', c: {d: 'e'}, f: 3},\n      nextState: {a: 4},\n    };\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      displayName: 'TestComponent',\n      ownerDataMap: expect.any(WeakMap),\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.function,\n            prevValue: input.prevProps.a,\n            nextValue: input.nextProps.a,\n          },\n          {\n            pathString: 'b',\n            diffType: diffTypes.different,\n            prevValue: input.prevProps.b,\n            nextValue: input.nextProps.b,\n          },\n          {\n            pathString: 'c',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevProps.c,\n            nextValue: input.nextProps.c,\n          },\n        ],\n        stateDifferences: [\n          {\n            pathString: 'a',\n            diffType: diffTypes.different,\n            prevValue: undefined,\n            nextValue: input.nextState.a,\n          },\n        ],\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n\n  test('deep equals and same object', () => {\n    const sameProp = {a: {b: 'c'}};\n\n    const prevProps = {className: 'aa', style: {width: '100%'}, sameProp};\n    const nextProps = {className: 'aa', style: {width: '100%'}, sameProp};\n\n    const input = getUpdateInfo({\n      Component: TestComponent,\n      displayName: getDisplayName(TestComponent),\n      prevProps,\n      prevState: null,\n      nextProps,\n      nextState: null,\n    });\n\n    const updateInfo = getUpdateInfo(input);\n\n    expect(updateInfo).toEqual({\n      ...input,\n      ownerDataMap: expect.any(WeakMap),\n      displayName: 'TestComponent',\n      reason: {\n        propsDifferences: [\n          {\n            pathString: 'style',\n            diffType: diffTypes.deepEquals,\n            prevValue: input.prevProps.style,\n            nextValue: input.nextProps.style,\n          },\n        ],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      },\n    });\n  });\n});\n"
  },
  {
    "path": "tests/hooks/childrenUsingHookResults.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nlet updateInfos = [];\n\n// eslint-disable-next-line no-console\nconst someFn = () => console.log('hi!');\n\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  }\n});\n\ndescribe('children using hook results', () => {\n  test('without dependencies', () => {\n    const AChild = () => <div>hi!</div>;\n    AChild.whyDidYouRender = true;\n\n    const ComponentWithMemoHook = () => {\n      const [currentState, setCurrentState] = React.useState({c: 'c'});\n      \n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'c'});\n      }, []);\n\n      const fnUseCallback = React.useCallback(() => someFn(currentState.c));\n      const fnUseMemo = React.useMemo(() => () => someFn(currentState.c));\n      const fnRegular = () => someFn(currentState.c);\n\n      return (\n        <AChild type=\"button\" fnRegular={fnRegular} fnUseMemo={fnUseMemo} fnUseCallback={fnUseCallback}/>\n      );\n    };\n\n    rtl.render(\n      <ComponentWithMemoHook/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: false,\n      stateDifferences: false,\n      propsDifferences: [\n        expect.objectContaining({\n          pathString: 'fnRegular',\n          diffType: 'function',\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseMemo',\n          diffType: 'function',\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseCallback',\n          diffType: 'function',\n        }),\n      ],\n      ownerDifferences: {\n        hookDifferences: [\n          {\n            differences: [\n              {\n                diffType: 'deepEquals',\n                nextValue: {c: 'c'},\n                pathString: '',\n                prevValue: {c: 'c'},\n              },\n            ],\n            hookName: 'useState',\n          },\n        ],\n        propsDifferences: false,\n        stateDifferences: false,\n      },\n    });\n  });\n\n  test('with different dependencies', () => {\n    const Child = () => <div>hi!</div>;\n    Child.whyDidYouRender = true;\n\n    const ComponentWithMemoHook = () => {\n      const [currentState, setCurrentState] = React.useState({c: 'c'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'd'});\n      }, []);\n\n      const fnUseCallback = React.useCallback(() => someFn(currentState.c), [currentState]);\n      const fnUseMemo = React.useMemo(() => () => someFn(currentState.c), [currentState]);\n      const fnRegular = () => someFn(currentState.c);\n\n      return (\n        <Child type=\"button\" fnRegular={fnRegular} fnUseMemo={fnUseMemo} fnUseCallback={fnUseCallback}/>\n      );\n    };\n\n    rtl.render(\n      <ComponentWithMemoHook/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: false,\n      stateDifferences: false,\n      propsDifferences: expect.arrayContaining([\n        expect.objectContaining({\n          pathString: 'fnRegular',\n          diffType: diffTypes.function,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseMemo',\n          diffType: diffTypes.different,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseCallback',\n          diffType: diffTypes.different,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseMemo:parent-hook-useMemo-deps',\n          diffType: diffTypes.different,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseCallback:parent-hook-useCallback-deps',\n          diffType: diffTypes.different,\n        }),\n      ]),\n      ownerDifferences: {\n        hookDifferences: [\n          {\n            differences: [\n              {\n                diffType: diffTypes.different,\n                pathString: '.c',\n                prevValue: 'c',\n                nextValue: 'd',\n              },\n              {\n                diffType: diffTypes.different,\n                pathString: '',\n                prevValue: {c: 'c'},\n                nextValue: {c: 'd'},\n              },\n            ],\n            hookName: 'useState',\n          },\n        ],\n        propsDifferences: false,\n        stateDifferences: false,\n      },\n    });\n  });\n\n  test('with deep Equals dependencies', () => {\n    const Child = () => <div>hi!</div>;\n    Child.whyDidYouRender = true;\n\n    const ComponentWithMemoHook = () => {\n      const [currentState, setCurrentState] = React.useState({c: 'c'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'c'});\n      }, []);\n\n      const fnUseCallback = React.useCallback(() => someFn(currentState.c), [currentState]);\n      const fnUseMemo = React.useMemo(() => () => someFn(currentState.c), [currentState]);\n      const fnRegular = () => someFn(currentState.c);\n\n      return (\n        <Child type=\"button\" fnRegular={fnRegular} fnUseMemo={fnUseMemo} fnUseCallback={fnUseCallback}/>\n      );\n    };\n\n    rtl.render(\n      <ComponentWithMemoHook/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: false,\n      stateDifferences: false,\n      propsDifferences: expect.arrayContaining([\n        expect.objectContaining({\n          pathString: 'fnRegular',\n          diffType: diffTypes.function,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseMemo',\n          diffType: diffTypes.function,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseCallback',\n          diffType: diffTypes.function,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseMemo:parent-hook-useMemo-deps',\n          diffType: diffTypes.deepEquals,\n        }),\n        expect.objectContaining({\n          pathString: 'fnUseCallback:parent-hook-useCallback-deps',\n          diffType: diffTypes.deepEquals,\n        }),\n      ]),\n      ownerDifferences: {\n        hookDifferences: [\n          {\n            differences: [\n              {\n                diffType: diffTypes.deepEquals,\n                pathString: '',\n                prevValue: {c: 'c'},\n                nextValue: {c: 'c'},\n              },\n            ],\n            hookName: 'useState',\n          },\n        ],\n        propsDifferences: false,\n        stateDifferences: false,\n      },\n    });\n  });\n});\n"
  },
  {
    "path": "tests/hooks/hooks.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\ndescribe('hooks - simple', () => {\n  describe('hooks - track', () => {\n    let updateInfos = [];\n\n    beforeEach(() => {\n      updateInfos = [];\n      whyDidYouRender(React, {\n        notifier: updateInfo => updateInfos.push(updateInfo),\n      });\n    });\n\n    afterEach(() => {\n      if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n        React.__REVERT_WHY_DID_YOU_RENDER__();\n      }\n    });\n\n    test('no whyDidYouRender=true', () => {\n      const ComponentWithHooks = ({a}) => {\n        const [currentState] = React.useState({b: 'b'});\n\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n\n      const {rerender} = rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n      rerender(\n        <ComponentWithHooks a={2}/>\n      );\n\n      expect(updateInfos).toHaveLength(0);\n    });\n\n    test('simple hooks tracking', () => {\n      const ComponentWithHooks = ({a}) => {\n        const [currentState, setCurrentState] = React.useState({b: 'b'});\n        React.useLayoutEffect(() => {\n          setCurrentState({b: 'b'});\n        }, []);\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n      ComponentWithHooks.whyDidYouRender = true;\n\n      rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n\n      expect(updateInfos).toHaveLength(1);\n    });\n\n    test('after removing WDYR', () => {\n      React.__REVERT_WHY_DID_YOU_RENDER__();\n\n      const ComponentWithHooks = ({a}) => {\n        const [currentState, setCurrentState] = React.useState({b: 'b'});\n        React.useLayoutEffect(() => {\n          setCurrentState({b: 'b'});\n        }, []);\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n      ComponentWithHooks.whyDidYouRender = true;\n\n      rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n\n      expect(updateInfos).toHaveLength(0);\n    });\n\n    test('track component', () => {\n      const ComponentWithHooks = ({a}) => {\n        const [currentState] = React.useState({b: 'b'});\n\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n\n      ComponentWithHooks.whyDidYouRender = true;\n\n      const {rerender} = rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n      rerender(\n        <ComponentWithHooks a={2}/>\n      );\n\n      expect(updateInfos).toHaveLength(1);\n      expect(updateInfos[0].reason).toEqual({\n        propsDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      });\n    });\n\n    test('track memoized component', () => {\n      const ComponentWithHooks = React.memo(({a}) => {\n        const [currentState] = React.useState({b: 'b'});\n\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      });\n\n      ComponentWithHooks.whyDidYouRender = true;\n\n      const {rerender} = rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n      rerender(\n        <ComponentWithHooks a={2}/>\n      );\n\n      expect(updateInfos).toHaveLength(1);\n      expect(updateInfos[0].reason).toEqual({\n        propsDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        stateDifferences: false,\n        hookDifferences: false,\n        ownerDifferences: false,\n      });\n    });\n  });\n\n  describe('hooks - do not track', () => {\n    let updateInfos = [];\n\n    beforeEach(() => {\n      updateInfos = [];\n      whyDidYouRender(React, {\n        notifier: updateInfo => updateInfos.push(updateInfo),\n        trackHooks: false,\n      });\n    });\n\n    afterEach(() => {\n      if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n        React.__REVERT_WHY_DID_YOU_RENDER__();\n      }\n    });\n\n    test('no whyDidYouRender=true', () => {\n      const ComponentWithHooks = ({a}) => {\n        const [currentState] = React.useState({b: 'b'});\n\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n\n      const {rerender} = rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n      rerender(\n        <ComponentWithHooks a={2}/>\n      );\n\n      expect(updateInfos).toHaveLength(0);\n    });\n\n    test('with whyDidYouRender=true', () => {\n      const ComponentWithHooks = ({a}) => {\n        const [currentState, setCurrentState] = React.useState({b: 'b'});\n        React.useLayoutEffect(() => {\n          setCurrentState({b: 'b'});\n        }, []);\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n      ComponentWithHooks.whyDidYouRender = true;\n\n      rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n\n      expect(updateInfos).toHaveLength(0);\n    });\n\n    test('after removing WDYR', () => {\n      React.__REVERT_WHY_DID_YOU_RENDER__();\n\n      const ComponentWithHooks = ({a}) => {\n        const [currentState, setCurrentState] = React.useState({b: 'b'});\n        React.useLayoutEffect(() => {\n          setCurrentState({b: 'b'});\n        }, []);\n        return (\n          <div>hi! {a} {currentState.b}</div>\n        );\n      };\n      ComponentWithHooks.whyDidYouRender = true;\n\n      rtl.render(\n        <ComponentWithHooks a={1}/>\n      );\n\n      expect(updateInfos).toHaveLength(0);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/hooks/useContext.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\ndescribe('hooks - useContext', () => {\n  let updateInfos = [];\n\n  beforeEach(() => {\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('same value', () => {\n    const MyContext = React.createContext('c');\n\n    const ComponentWithContextHook = ({a, b}) => {\n      const valueFromContext = React.useContext(MyContext);\n\n      return (\n        <div>hi! {a} {b} {valueFromContext}</div>\n      );\n    };\n    ComponentWithContextHook.whyDidYouRender = true;\n\n    const OuterComponent = () => {\n      const [currentState, setCurrentState] = React.useState('c');\n\n      React.useLayoutEffect(() => {\n        setCurrentState('c');\n      }, []);\n\n      return (\n        <MyContext.Provider value={currentState}>\n          <div>\n            <ComponentWithContextHook a={1} b={2}/>\n          </div>\n        </MyContext.Provider>\n      );\n    };\n\n    rtl.render(\n      <OuterComponent/>\n    );\n\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('deep equals - memoized', () => {\n    const MyContext = React.createContext({c: 'c'});\n\n    const ComponentWithContextHook = React.memo(({a, b}) => {\n      const valueFromContext = React.useContext(MyContext);\n\n      return (\n        <div>hi! {a} {b} {valueFromContext.c}</div>\n      );\n    });\n    ComponentWithContextHook.whyDidYouRender = true;\n\n    const OuterComponent = () => {\n      const [currentState, setCurrentState] = React.useState({c: 'c'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'c'});\n      }, []);\n\n      return (\n        <MyContext.Provider value={currentState}>\n          <div>\n            <ComponentWithContextHook a={1} b={2}/>\n          </div>\n        </MyContext.Provider>\n      );\n    };\n\n    rtl.render(\n      <OuterComponent/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {c: 'c'},\n        prevValue: {c: 'c'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n\n  test('deep equals - not memoized', () => {\n    const MyContext = React.createContext({c: 'c'});\n\n    const ComponentWithContextHook = ({a, b}) => {\n      const valueFromContext = React.useContext(MyContext);\n\n      return (\n        <div>hi! {a} {b} {valueFromContext.c}</div>\n      );\n    };\n    ComponentWithContextHook.whyDidYouRender = true;\n\n    const OuterComponent = () => {\n      const [currentState, setCurrentState] = React.useState({c: 'c'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({c: 'c'});\n      }, []);\n\n      return (\n        <MyContext value={currentState}>\n          <div>\n            <ComponentWithContextHook a={1} b={2}/>\n          </div>\n        </MyContext>\n      );\n    };\n\n    rtl.render(\n      <OuterComponent/>\n    );\n\n    expect(updateInfos).toHaveLength(2);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: false,\n      propsDifferences: [],\n      stateDifferences: false,\n      ownerDifferences: {\n        hookDifferences: [{\n          differences: [{\n            diffType: diffTypes.deepEquals,\n            pathString: '',\n            nextValue: {c: 'c'},\n            prevValue: {c: 'c'},\n          }],\n          hookName: 'useState',\n        }],\n        propsDifferences: false,\n        stateDifferences: false,\n      },\n    });\n    expect(updateInfos[1]).toEqual(expect.objectContaining({\n      hookName: 'useContext',\n      reason: {\n        hookDifferences: [{\n          diffType: diffTypes.deepEquals,\n          pathString: '',\n          nextValue: {c: 'c'},\n          prevValue: {c: 'c'},\n        }],\n        propsDifferences: false,\n        stateDifferences: false,\n        ownerDifferences: false,\n      }\n    }));\n  });\n});\n"
  },
  {
    "path": "tests/hooks/useReducer.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\ndescribe('hooks - useReducer', () => {\n  let updateInfos = [];\n\n  beforeEach(() => {\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('same value', () => {\n    const initialState = {b: 'b'};\n\n    function reducer() {\n      return initialState;\n    }\n\n    let numOfRenders = 0;\n    const ComponentWithHooks = () => {\n      numOfRenders++;\n      const [state, dispatch] = React.useReducer(reducer, initialState);\n\n      React.useLayoutEffect(() => {\n        dispatch({type: 'something'});\n      }, []);\n\n      return (\n        <div>hi! {state.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks/>\n    );\n\n    expect(numOfRenders).toBe(2);\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('different value', () => {\n    const initialState = {b: 'b'};\n\n    function reducer() {\n      return {a: 'a'};\n    }\n\n    const ComponentWithHooks = ({a}) => {\n      const [state, dispatch] = React.useReducer(reducer, initialState);\n\n      React.useLayoutEffect(() => {\n        dispatch({type: 'something'});\n      }, []);\n\n      return (\n        <div data-testid=\"test\">hi! {a} {state.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        pathString: '',\n        diffType: diffTypes.different,\n        prevValue: {b: 'b'},\n        nextValue: {a: 'a'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n\n  test('deep equals', () => {\n    const initialState = {b: 'b'};\n\n    function reducer() {\n      return {b: 'b'};\n    }\n\n    const ComponentWithHooks = ({a}) => {\n      const [state, dispatch] = React.useReducer(reducer, initialState);\n\n      React.useLayoutEffect(() => {\n        dispatch({type: 'something'});\n      }, []);\n\n      return (\n        <div>hi! {a} {state.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {b: 'b'},\n        prevValue: {b: 'b'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n});\n"
  },
  {
    "path": "tests/hooks/useState.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\ndescribe('hooks - useState', () => {\n  let updateInfos = [];\n\n  beforeEach(() => {\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('setState - same value', () => {\n    const initialState = {b: 'b'};\n    const ComponentWithHooks = () => {\n      const [currentState, setCurrentState] = React.useState(initialState);\n\n      React.useLayoutEffect(() => {\n        setCurrentState(initialState);\n      }, []);\n\n      return (\n        <div>hi! {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks/>\n    );\n\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('setState of different values', () => {\n    const ComponentWithHooks = () => {\n      const [currentState, setCurrentState] = React.useState({b: 'b'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({b: 'c'});\n      }, []);\n\n      return (\n        <div>hi! {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: false,\n      stateDifferences: false,\n      hookDifferences: [\n        {\n          pathString: '.b',\n          diffType: diffTypes.different,\n          prevValue: 'b',\n          nextValue: 'c',\n        },\n        {\n          pathString: '',\n          diffType: diffTypes.different,\n          prevValue: {b: 'b'},\n          nextValue: {b: 'c'},\n        },\n      ],\n      ownerDifferences: false,\n    });\n  });\n\n  test('setState of deep equals values', () => {\n    const ComponentWithHooks = ({a}) => {\n      const [currentState, setCurrentState] = React.useState({b: 'b'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({b: 'b'});\n      }, []);\n\n      return (\n        <div>hi! {a} {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: false,\n      stateDifferences: false,\n      hookDifferences: [{\n        pathString: '',\n        diffType: diffTypes.deepEquals,\n        prevValue: {b: 'b'},\n        nextValue: {b: 'b'},\n      }],\n      ownerDifferences: false,\n    });\n  });\n});\n\ndescribe('track hooks', () => {\n  let updateInfos = [];\n\n  beforeEach(() => {\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n      React.__REVERT_WHY_DID_YOU_RENDER__();\n    }\n  });\n\n  test('same value', () => {\n    const value = {b: 'b'};\n\n    let effectCalled = false;\n\n    const ComponentWithHooks = ({a}) => {\n      const [currentState, setCurrentState] = React.useState(value);\n\n      React.useLayoutEffect(() => {\n        effectCalled = true;\n        setCurrentState(value);\n      }, []);\n\n      return (\n        <div>hi! {a} {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(0);\n    expect(effectCalled).toBeTruthy();\n  });\n\n  test('different (falsy to truthy)', () => {\n    const ComponentWithHooks = () => {\n      const [currentResult, setCurrentState] = React.useState(false);\n      const result = React.useMemo(() => currentResult, [currentResult]);\n\n      React.useLayoutEffect(() => {\n        setCurrentState(true);\n      }, []);\n\n      return (\n        <div>hi! {result}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = {\n      logOnDifferentValues: true,\n    };\n\n    rtl.render(\n      <ComponentWithHooks/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n  });\n\n  test('deep equals', () => {\n    const ComponentWithHooks = ({a}) => {\n      const [currentState, setCurrentState] = React.useState({b: 'b'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({b: 'b'});\n      }, []);\n\n      return (\n        <div>hi! {a} {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {b: 'b'},\n        prevValue: {b: 'b'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n\n  test('deep equals direct import', () => {\n    const ComponentWithHooks = ({a}) => {\n      const [currentState, setCurrentState] = React.useState({b: 'b'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState({b: 'b'});\n      }, []);\n\n      return (\n        <div>hi! {a} {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {b: 'b'},\n        prevValue: {b: 'b'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n\n  test('many deep equals direct import', () => {\n    const ComponentWithHooks = ({a}) => {\n      const [currentStateA, setCurrentStateA] = React.useState({a: 'a'});\n      const [currentStateB, setCurrentStateB] = React.useState({b: 'b'});\n      const [currentStateC, setCurrentStateC] = React.useState({c: 'c'});\n      const [currentStateD, setCurrentStateD] = React.useState({d: 'd'});\n      const [currentStateE, setCurrentStateE] = React.useState({e: 'e'});\n\n      React.useLayoutEffect(() => {\n        setCurrentStateA({a: 'a'});\n        setCurrentStateB({b: 'b'});\n        setCurrentStateC({c: 'c'});\n        setCurrentStateD({d: 'd'});\n        setCurrentStateE({e: 'e'});\n      }, []);\n\n      return (\n        <div>hi! {a} {currentStateA.a} {currentStateB.b} {currentStateC.c} {currentStateD.d} {currentStateE.e}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(5);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {a: 'a'},\n        prevValue: {a: 'a'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n    expect(updateInfos[1].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {b: 'b'},\n        prevValue: {b: 'b'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n    expect(updateInfos[2].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {c: 'c'},\n        prevValue: {c: 'c'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n    expect(updateInfos[3].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {d: 'd'},\n        prevValue: {d: 'd'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n    expect(updateInfos[4].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {e: 'e'},\n        prevValue: {e: 'e'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n\n  test('deep equals functional use', () => {\n    const ComponentWithHooks = ({a}) => {\n      const [currentState, setCurrentState] = React.useState({b: 'b'});\n\n      React.useLayoutEffect(() => {\n        setCurrentState(() => ({b: 'b'}));\n      }, []);\n\n      return (\n        <div>hi! {a} {currentState.b}</div>\n      );\n    };\n\n    ComponentWithHooks.whyDidYouRender = true;\n\n    rtl.render(\n      <ComponentWithHooks a={1}/>\n    );\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {b: 'b'},\n        prevValue: {b: 'b'},\n      }],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n});\n"
  },
  {
    "path": "tests/hooks/useSyncExternalStore.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\ndescribe('hooks - useSyncExternalStore', () => {\n  let updateInfos = [];\n\n  function createSimpleStore(initialState) {\n    let state = initialState;\n    const listeners = new Set();\n\n    return {\n      getState: () => state,\n      setState: (newState) => {\n        state = newState;\n        listeners.forEach((listener) => listener());\n      },\n      subscribe: (listener) => {\n        listeners.add(listener);\n        return () => listeners.delete(listener);\n      },\n    };\n  }\n\n  beforeEach(() => {\n    updateInfos = [];\n\n    whyDidYouRender(React, {\n      notifier: (updateInfo) => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  test('same value', () => {\n    const store = createSimpleStore('c');\n\n    const ComponentWithSyncExternalStore = ({a, b}) => {\n      const valueFromStore = React.useSyncExternalStore(\n        store.subscribe,\n        store.getState\n      );\n\n      return (\n        <div>\n          hi! {a} {b} {valueFromStore}\n        </div>\n      );\n    };\n    ComponentWithSyncExternalStore.whyDidYouRender = true;\n\n    const OuterComponent = () => {\n      React.useLayoutEffect(() => {\n        store.setState('c');\n      }, []);\n\n      return (\n        <div>\n          <ComponentWithSyncExternalStore a={1} b={2} />\n        </div>\n      );\n    };\n\n    rtl.render(<OuterComponent />);\n\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('deep equals', () => {\n    const store = createSimpleStore({c: 'c'});\n\n    const ComponentWithSyncExternalStore = ({a, b}) => {\n      const valueFromStore = React.useSyncExternalStore(\n        store.subscribe,\n        store.getState\n      );\n\n      return (\n        <div>\n          hi! {a} {b} {valueFromStore.c}\n        </div>\n      );\n    };\n    ComponentWithSyncExternalStore.whyDidYouRender = true;\n\n    const OuterComponent = () => {\n      React.useLayoutEffect(() => {\n        store.setState({c: 'c'});\n      }, []);\n\n      return (\n        <div>\n          <ComponentWithSyncExternalStore a={1} b={2} />\n        </div>\n      );\n    };\n\n    rtl.render(<OuterComponent />);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      hookDifferences: [\n        {\n          diffType: diffTypes.deepEquals,\n          pathString: '',\n          nextValue: {c: 'c'},\n          prevValue: {c: 'c'},\n        },\n      ],\n      propsDifferences: false,\n      stateDifferences: false,\n      ownerDifferences: false,\n    });\n  });\n});\n"
  },
  {
    "path": "tests/index.test.js",
    "content": "import React from 'react';\nimport ReactDOMServer from 'react-dom/server';\nimport * as rtl from '@testing-library/react';\nimport _ from 'lodash';\nimport whyDidYouRender from '~';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('dont swallow errors', () => {\n  const BrokenComponent = React.memo(null);\n  BrokenComponent.whyDidYouRender = true;\n\n  const mountBrokenComponent = () => {\n    rtl.render(\n      <BrokenComponent/>\n    );\n  };\n\n  expect(mountBrokenComponent).toThrow(/expected a string.*but got.*null/);\n\n  global.flushConsoleOutput()\n    .map(output => ({\n      ...output,\n      args: output.args.map(a => _.isError(a) ? JSON.stringify(a.message) : a)\n    }))\n    .forEach(output => {\n      expect(output).toEqual({\n        level: 'error',\n        args: expect.arrayContaining([\n          expect.stringMatching(/(memo: The first argument must be a component|propTypes|error boundary)/),\n        ]),\n      });\n    });\n});\n\ntest('render to static markup', () => {\n  class MyComponent extends React.Component {\n    static whyDidYouRender = true;\n    render() {\n      return (\n        <div>\n          hi!\n        </div>\n      );\n    }\n  }\n  const string = ReactDOMServer.renderToStaticMarkup(<MyComponent/>);\n  expect(string).toBe('<div>hi!</div>');\n});\n"
  },
  {
    "path": "tests/librariesTests/react-redux.test.js",
    "content": "import React from 'react';\nimport {legacy_createStore as createStore} from 'redux';\nimport {cloneDeep} from 'lodash';\nimport * as rtl from '@testing-library/react';\n\nimport {diffTypes} from '~/consts';\n\nimport whyDidYouRender from '~';\n\nconst ReactRedux = {...require('react-redux')};\nconst {connect, Provider} = ReactRedux;\n\ndescribe('react-redux - simple', () => {\n  const initialState = {a: {b: 'c'}};\n\n  const rootReducer = (state, action) => {\n    if (action.type === 'differentState') {\n      return {a: {b: 'd'}};\n    }\n\n    if (action.type === 'deepEqlState') {\n      return cloneDeep(state);\n    }\n\n    return state;\n  };\n\n  let store;\n  let updateInfos;\n\n  beforeEach(() => {\n    store = createStore(rootReducer, initialState);\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n    });\n  });\n\n  afterEach(() => {\n    if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n      React.__REVERT_WHY_DID_YOU_RENDER__();\n    }\n  });\n\n  test('same state after dispatch', () => {\n    const SimpleComponent = ({a}) => (\n      <div data-testid=\"foo\">{a.b}</div>\n    );\n    const ConnectedSimpleComponent = connect(\n      state => ({a: state.a})\n    )(SimpleComponent);\n\n    SimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'sameState'});\n    });\n\n    expect(store.getState().a.b).toBe('c');\n\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('different state after dispatch', () => {\n    const SimpleComponent = ({a}) => (\n      <div data-testid=\"foo\">{a.b}</div>\n    );\n    const ConnectedSimpleComponent = connect(\n      state => ({a: state.a})\n    )(SimpleComponent);\n\n    SimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'differentState'});\n    });\n\n    expect(store.getState().a.b).toBe('d');\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [\n        expect.objectContaining({diffType: diffTypes.different}),\n        expect.objectContaining({diffType: diffTypes.different}),\n      ],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: expect.anything(),\n    });\n  });\n\n  test('deep equals state after dispatch', () => {\n    const SimpleComponent = ({a}) => (\n      <div data-testid=\"foo\">\n        {a.b}\n      </div>\n    );\n    \n    const ConnectedSimpleComponent = connect(\n      state => ({a: state.a})\n    )(SimpleComponent);\n\n    SimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'deepEqlState'});\n    });\n\n    expect(store.getState().a.b).toBe('c');\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [\n        expect.objectContaining({diffType: diffTypes.deepEquals}),\n      ],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: expect.anything(),\n    });\n  });\n});\n\ndescribe('react-redux - hooks', () => {\n  const initialState = {a: {b: 'c'}};\n\n  const rootReducer = (state, action) => {\n    if (action.type === 'differentState') {\n      return {a: {b: 'd'}};\n    }\n\n    if (action.type === 'deepEqlState') {\n      return cloneDeep(state);\n    }\n\n    return state;\n  };\n\n  let store;\n  let updateInfos;\n\n  beforeEach(() => {\n    store = createStore(rootReducer, initialState);\n    updateInfos = [];\n    whyDidYouRender(React, {\n      notifier: updateInfo => updateInfos.push(updateInfo),\n      trackExtraHooks: [\n        [ReactRedux, 'useSelector'],\n      ],\n    });\n  });\n\n  afterEach(() => {\n    if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n      React.__REVERT_WHY_DID_YOU_RENDER__();\n    }\n  });\n\n  test('same state after dispatch', () => {\n    const ConnectedSimpleComponent = () => {\n      const a = ReactRedux.useSelector(state => state);\n      return (\n        <div data-testid=\"foo\">{a.b}</div>\n      );\n    };\n    ConnectedSimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'sameState'});\n    });\n\n    expect(store.getState().a.b).toBe('c');\n\n    expect(updateInfos).toHaveLength(0);\n  });\n\n  test('different state after dispatch', () => {\n    const ConnectedSimpleComponent = () => {\n      const a = ReactRedux.useSelector(state => state.a);\n      return (\n        <div data-testid=\"foo\">{a.b}</div>\n      );\n    };\n\n    ConnectedSimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'differentState'});\n    });\n\n    expect(store.getState().a.b).toBe('d');\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0]).toEqual(expect.objectContaining({\n      hookName: 'useSelector',\n      reason: {\n        propsDifferences: false,\n        stateDifferences: false,\n        hookDifferences: [\n          {diffType: diffTypes.different, pathString: '.b', prevValue: 'c', nextValue: 'd'},\n          {diffType: diffTypes.different, pathString: '', prevValue: {b: 'c'}, nextValue: {b: 'd'}},\n        ],\n        ownerDifferences: false,\n      },\n    }));\n  });\n\n  test('deep equals state after dispatch', () => {\n    const ConnectedSimpleComponent = () => {\n      const a = ReactRedux.useSelector(state => state.a);\n      return (\n        <div data-testid=\"foo\">\n          {a.b}\n        </div>\n      );\n    };\n    ConnectedSimpleComponent.whyDidYouRender = true;\n\n    const Main = () => (\n      <Provider store={store}>\n        <ConnectedSimpleComponent/>\n      </Provider>\n    );\n\n    rtl.render(<Main/>);\n\n    expect(store.getState().a.b).toBe('c');\n\n    rtl.act(() => {\n      store.dispatch({type: 'deepEqlState'});\n    });\n\n    expect(store.getState().a.b).toBe('c');\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0]).toEqual(expect.objectContaining({\n      hookName: 'useSelector',\n      reason: {\n        propsDifferences: false,\n        stateDifferences: false,\n        hookDifferences: [\n          {diffType: diffTypes.deepEquals, pathString: '', prevValue: {b: 'c'}, nextValue: {b: 'c'}},\n        ],\n        ownerDifferences: false,\n      },\n    }));\n  });\n});\n"
  },
  {
    "path": "tests/librariesTests/react-router-dom.test.js",
    "content": "import React from 'react';\nimport {legacy_createStore as createStore} from 'redux';\nimport {\n  BrowserRouter,\n  useLocation,\n  Routes,\n  Route,\n} from 'react-router-dom';\nimport {connect, Provider} from 'react-redux';\nimport {cloneDeep} from 'lodash';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ndescribe('react-router-dom', () => {\n  test('simple', () => {\n    const InnerComp = ({a}) => {\n      const location = useLocation();\n\n      const [state, setState] = React.useState(0);\n      React.useLayoutEffect(() => {\n        setState(a => a + 1);\n      }, []);\n\n      // eslint-disable-next-line no-console\n      console.log(`location is: ${location.pathname}`);\n\n      return (\n        <div>hi! {JSON.stringify(a)} {state}</div>\n      );\n    };\n\n    InnerComp.whyDidYouRender = true;\n\n    const Comp = () => (\n      <BrowserRouter>\n        <Routes>\n          <Route exact path=\"/\" element={<InnerComp a={{b: 'c'}}/>}/>\n        </Routes>\n      </BrowserRouter>\n    );\n\n    const {rerender} = rtl.render(<Comp/>);\n\n    rerender(<Comp/>);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toEqual([\n      expect.objectContaining({args: ['location is: /']}),\n      expect.objectContaining({args: ['location is: /']}),\n      expect.objectContaining({args: ['location is: /']}),\n    ]);\n\n    expect(updateInfos).toHaveLength(2);\n    expect(updateInfos).toEqual([\n      expect.objectContaining({\n        displayName: 'InnerComp',\n        hookName: 'useState',\n      }),\n      expect.objectContaining({\n        displayName: 'InnerComp',\n        reason: {\n          hookDifferences: false,\n          stateDifferences: false,\n          propsDifferences: [{\n            diffType: 'deepEquals',\n            nextValue: {b: 'c'},\n            pathString: 'a',\n            prevValue: {b: 'c'},\n          }],\n          ownerDifferences: {\n            hookDifferences: false,\n            propsDifferences: false,\n            stateDifferences: false\n          }\n        }\n      })\n    ]);\n  });\n\n  test('with redux', () => {\n    const initialState = {a: {b: 'c'}};\n\n    const rootReducer = (state, action) => {\n      if (action.type === 'differentState') {\n        return {a: {b: 'd'}};\n      }\n\n      if (action.type === 'deepEqlState') {\n        return cloneDeep(state);\n      }\n\n      return state;\n    };\n\n    const store = createStore(rootReducer, initialState);\n\n    const InnerFn = ({a, setDeepEqlState}) => {\n      const location = useLocation();\n\n      React.useLayoutEffect(() => {\n        setDeepEqlState();\n      }, []);\n\n      // eslint-disable-next-line no-console\n      console.log(`location is: ${location.pathname}`);\n\n      return <div>hi! {a.b}</div>;\n    };\n\n    const InnerComp = connect(\n      state => ({a: state.a}),\n      {setDeepEqlState: () => ({type: 'deepEqlState'})}\n    )(InnerFn);\n\n    InnerFn.whyDidYouRender = true;\n\n    const Comp = () => (\n      <Provider store={store}>\n        <BrowserRouter>\n          <Routes>\n            <Route exact path=\"/\" element={<InnerComp/>}/>\n          </Routes>\n        </BrowserRouter>\n      </Provider>\n    );\n\n    rtl.render(<Comp/>);\n\n    const consoleOutputs = flushConsoleOutput();\n    expect(consoleOutputs).toEqual([\n      expect.objectContaining({args: ['location is: /']}),\n      expect.objectContaining({args: ['location is: /']}),\n    ]);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [{\n        diffType: diffTypes.deepEquals,\n        pathString: 'a',\n        prevValue: {b: 'c'},\n        nextValue: {b: 'c'},\n      }],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: expect.anything(),\n    });\n  });\n});\n"
  },
  {
    "path": "tests/librariesTests/styled-components.test.js",
    "content": " \nimport React from 'react';\nimport styled from 'styled-components/dist/styled-components.js';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('simple styled-components', () => {\n  const InnerComponent = () => <div>foobar</div>;\n  const StyledInnerComponent = styled(InnerComponent)``;\n\n  StyledInnerComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <StyledInnerComponent a={[]}/>\n  );\n  rerender(\n    <StyledInnerComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('styled-components wrap of a memoized component', () => {\n  const InnerComponent = React.memo(() => <div>foobar</div>);\n  const StyledInnerComponent = styled(InnerComponent)``;\n\n  StyledInnerComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <StyledInnerComponent a={[]}/>\n  );\n  rerender(\n    <StyledInnerComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('styled-components with forward ref', () => {\n  const InnerComponent = React.forwardRef((props, ref) =>\n    <div ref={ref}>foobar</div>\n  );\n\n  const Styled = styled(InnerComponent)``;\n\n  Styled.whyDidYouRender = true;\n\n  const Wrapper = (props) => {\n    const ref = React.useRef(null);\n    return <Styled {...props} ref={ref} />;\n  };\n\n  const {rerender} = rtl.render(\n    <Wrapper a={[]}/>\n  );\n  rerender(\n    <Wrapper a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: {\n      hookDifferences: false,\n      propsDifferences: [{\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      }],\n      stateDifferences: false,\n    },\n  });\n});\n\ntest('styled-components with memoized forward ref', () => {\n  const InnerComponent = React.memo(\n    React.forwardRef((props, ref) =>\n      <div ref={ref}>foobar</div>\n    )\n  );\n\n  const StyledInnerComponent = styled(InnerComponent)``;\n\n  StyledInnerComponent.whyDidYouRender = true;\n\n  const Wrapper = (props) => {\n    const ref = React.useRef(null);\n    return <StyledInnerComponent {...props} ref={ref} />;\n  };\n\n  const {rerender} = rtl.render(\n    <Wrapper a={[]}/>\n  );\n  rerender(\n    <Wrapper a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: {\n      hookDifferences: false,\n      propsDifferences: [{\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      }],\n      stateDifferences: false,\n    },\n  });\n});\n"
  },
  {
    "path": "tests/logOnDifferentValues.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    include: [/.*/],\n    logOnDifferentValues: true,\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('hook value change', () => {\n  const Foo = React.memo(function Foo(props) {\n    return (\n      <div>\n        Foo {props.a.v}\n      </div>\n    );\n  });\n\n  const App = React.memo(function App() {\n    const [text, setText] = React.useState('Click me');\n\n    return (\n      <div className=\"App\">\n        <button\n          onClick={() => setText(state => state + '.')}\n          data-testid=\"button\"\n        >\n          {text}\n        </button>\n        <hr/>\n        <Foo a={{v: '1'}}/>\n        <Foo a={{v: '1'}}/>\n      </div>\n    );\n  });\n\n  const {getByTestId} = rtl.render(\n    <App/>\n  );\n\n  const button = getByTestId('button');\n  rtl.fireEvent.click(button);\n\n  expect(updateInfos).toEqual([\n    expect.objectContaining({displayName: 'App'}),\n    expect.objectContaining({displayName: 'Foo'}),\n    expect.objectContaining({displayName: 'Foo'}),\n  ]);\n});\n\ntest('Non simple objects', () => {\n  const Foo = React.memo(function Foo({error}) {\n    return (\n      <div>\n        <h1>{error.message}</h1>\n        <p>{error.stack}</p>\n      </div>\n    );\n  });\n\n  const App = React.memo(function App() {\n    const [text, setText] = React.useState('Click me');\n\n    return (\n      <div className=\"App\">\n        <button\n          onClick={() => setText(state => state + '.')}\n          data-testid=\"button\"\n        >\n          {text}\n        </button>\n        <hr/>\n        <Foo error={new Error('message')}/>\n      </div>\n    );\n  });\n\n  const {getByTestId} = rtl.render(\n    <App/>\n  );\n\n  const button = getByTestId('button');\n  rtl.fireEvent.click(button);\n\n  expect(updateInfos[1].reason.propsDifferences[0]).toEqual(\n    expect.objectContaining({diffType: 'deepEquals', 'pathString': 'error'}),\n  );\n});\n"
  },
  {
    "path": "tests/logOwnerReasons.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nlet updateInfos = [];\n\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  if (React.__REVERT_WHY_DID_YOU_RENDER__) {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  }\n});\n\nfunction createOwners(Child) {\n  const FunctionalOwner = () => <Child />;\n\n  class ClassOwner extends React.Component {\n    state = {a: 1};\n    componentDidMount() {\n      this.setState({a: 2});\n    }\n\n    render() {\n      return <Child />;\n    }\n  }\n\n  function HooksOwner() {\n    /* eslint-disable no-unused-vars */\n    const [a, setA] = React.useState(1);\n    const [b, setB] = React.useState(1);\n    /* eslint-enable */\n    React.useEffect(() => {\n      setA(2);\n      setB(2);\n    }, []);\n\n    return <Child />;\n  }\n\n  return {FunctionalOwner, ClassOwner, HooksOwner};\n}\n\nfunction CloneOwner({children}) {\n  const [, setA] = React.useState(1);\n  const [, setB] = React.useState(1);\n  React.useLayoutEffect(() => {\n    setA(2);\n    setB(2);\n  }, []);\n\n  return React.cloneElement(children);\n}\n\ndescribe('logOwnerReasons - function child', () => {\n  const Child = () => null;\n  Child.whyDidYouRender = true;\n\n  const {FunctionalOwner, ClassOwner, HooksOwner} = createOwners(Child);\n\n  test('owner props changed', () => {\n    const {rerender} = rtl.render(<FunctionalOwner a={1}/>);\n    rerender(<FunctionalOwner a={2} />);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        stateDifferences: false,\n        hookDifferences: false,\n      },\n    });\n  });\n\n  test('owner state changed', () => {\n    rtl.render(<ClassOwner/>);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: false,\n        stateDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        hookDifferences: false,\n      },\n    });\n  });\n\n  test('owner hooks changed', () => {\n    rtl.render(<HooksOwner/>);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: false,\n        stateDifferences: false,\n        hookDifferences: [\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n        ],\n      },\n    });\n  });\n\n  test('owner state updated during render', () => {\n    function DerivedStateOwner({ready}) {\n      const [wasReady, setWasReady] = React.useState(ready);\n      if (ready && !wasReady) {\n        setWasReady(true);\n      }\n\n      return <Child />;\n    }\n    const {rerender} = rtl.render(<DerivedStateOwner ready={false}/>);\n    rerender(<DerivedStateOwner ready/>);\n    rerender(<DerivedStateOwner ready={false}/>);\n\n    expect(updateInfos).toHaveLength(2);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: [{\n          pathString: 'ready',\n          diffType: diffTypes.different,\n          prevValue: false,\n          nextValue: true,\n        }],\n        stateDifferences: false,\n        hookDifferences: [\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: false,\n              nextValue: true,\n            }],\n          },\n        ],\n      },\n    });\n    expect(updateInfos[1].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: [{\n          pathString: 'ready',\n          diffType: diffTypes.different,\n          prevValue: true,\n          nextValue: false,\n        }],\n        stateDifferences: false,\n        hookDifferences: [{hookName: 'useState', differences: false}],\n      },\n    });\n  });\n\n  test('owner uses cloneElement', () => {\n    rtl.render(<CloneOwner><Child/></CloneOwner>);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: false,\n        stateDifferences: false,\n        hookDifferences: [\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n        ],\n      },\n    });\n  });\n});\n\n\ndescribe('logOwnerReasons - class child', () => {\n  class Child extends React.Component {\n    static whyDidYouRender = true;\n    render() {\n      return null;\n    }\n  }\n\n  const {FunctionalOwner, ClassOwner, HooksOwner} = createOwners(Child);\n\n  test('owner props changed', () => {\n    const {rerender} = rtl.render(<FunctionalOwner a={1}/>);\n    rerender(<FunctionalOwner a={2} />);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        stateDifferences: false,\n        hookDifferences: false,\n      },\n    });\n  });\n\n  test('owner state changed', () => {\n    rtl.render(<ClassOwner/>);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: false,\n        stateDifferences: [{\n          pathString: 'a',\n          diffType: diffTypes.different,\n          prevValue: 1,\n          nextValue: 2,\n        }],\n        hookDifferences: false,\n      },\n    });\n  });\n\n  test('owner hooks changed', () => {\n    rtl.render(<HooksOwner/>);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: {\n        propsDifferences: false,\n        stateDifferences: false,\n        hookDifferences: [\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n          {\n            hookName: 'useState',\n            differences: [{\n              pathString: '',\n              diffType: diffTypes.different,\n              prevValue: 1,\n              nextValue: 2,\n            }],\n          },\n        ],\n      },\n    });\n  });\n});\n"
  },
  {
    "path": "tests/normalizeOptions.test.js",
    "content": "/*  eslint-disable no-console */\nimport normalizeOptions from '~/normalizeOptions';\n\ntest('Empty options works', () => {\n  const options = normalizeOptions();\n  expect(options.consoleLog).toBe(console.log);\n});\n\ntest('User can rewrite options', () => {\n  const ownNotifier = () => {};\n  const userOptions = {\n    notifier: ownNotifier,\n  };\n  const options = normalizeOptions(userOptions);\n  expect(options.notifier).toBe(ownNotifier);\n  expect(options.consoleLog).toBe(console.log);\n});\n"
  },
  {
    "path": "tests/patches/patchClassComponent.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\nimport createReactClass from 'create-react-class';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nclass TestComponent extends React.Component {\n  static whyDidYouRender = true;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nconst createStateTestComponent = (initialState, newState) => {\n  return class StateTestComponent extends React.Component {\n    static whyDidYouRender = true;\n    state = initialState;\n    componentDidMount() {\n      this.setState(newState);\n    }\n    render() {\n      return <div>hi!</div>;\n    }\n  };\n};\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('Empty props and state', () => {\n  const {rerender} = rtl.render(\n    <TestComponent/>\n  );\n  rerender(\n    <TestComponent/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Same props', () => {\n  const {rerender} = rtl.render(\n    <TestComponent a={1}/>\n  );\n  rerender(\n    <TestComponent a={1}/>\n  );\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n  expect(updateInfos).toHaveLength(1);\n});\n\ntest('Same state', () => {\n  const StateTestComponent = createStateTestComponent({a: 1}, {a: 1});\n  rtl.render(\n    <StateTestComponent/>\n  );\n\n  return Promise.resolve()\n    .then(() => {\n      expect(updateInfos[0].reason).toEqual({\n        propsDifferences: false,\n        stateDifferences: [],\n        hookDifferences: false,\n        ownerDifferences: false,\n      });\n      expect(updateInfos).toHaveLength(1);\n    });\n});\n\ntest('Props change', () => {\n  const {rerender} = rtl.render(\n    <TestComponent a={1}/>\n  );\n  rerender(\n    <TestComponent a={2}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('With implemented \"componentDidUpdate()\"', () => {\n  let innerComponentDidUpdateCalled = false;\n  class OwnTestComponent extends React.Component {\n    static whyDidYouRender = true;\n    componentDidUpdate() {\n      innerComponentDidUpdateCalled = true;\n    }\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const {rerender} = rtl.render(\n    <OwnTestComponent a={1}/>\n  );\n  rerender(\n    <OwnTestComponent a={2}/>\n  );\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n  expect(innerComponentDidUpdateCalled).toBe(true);\n  expect(updateInfos).toHaveLength(1);\n});\n\ntest('With render as an arrow function', () => {\n  class OwnTestComponent extends React.Component {\n    static whyDidYouRender = true;\n    componentDidMount() {\n      this.setState({c: 'c'});\n    }\n    render = () => {\n      return <div>hi!</div>;\n    };\n  }\n\n  const {rerender} = rtl.render(\n    <OwnTestComponent a={1}/>\n  );\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: [{\n      diffType: diffTypes.different,\n      nextValue: 'c',\n      pathString: 'c',\n      prevValue: undefined,\n    }],\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  rerender(\n    <OwnTestComponent a={2}/>\n  );\n\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos).toHaveLength(2);\n});\n\ntest('With render as a binded function', () => {\n  class OwnTestComponent extends React.Component {\n    static whyDidYouRender = true;\n    constructor(props, context) {\n      super(props, context);\n      this.render = this.render.bind(this);\n    }\n    componentDidMount() {\n      this.setState({c: 'c'});\n    }\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const {rerender} = rtl.render(\n    <OwnTestComponent a={1}/>\n  );\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: [{\n      diffType: diffTypes.different,\n      nextValue: 'c',\n      pathString: 'c',\n      prevValue: undefined,\n    }],\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  rerender(\n    <OwnTestComponent a={2}/>\n  );\n\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos).toHaveLength(2);\n});\n\ntest('With implemented \"componentDidUpdate()\" with a snapshot - not tracked', () => {\n  let resolve = false;\n  class OwnTestComponent extends React.Component {\n    getSnapshotBeforeUpdate() {\n      return true;\n    }\n    componentDidUpdate(prevProps, prevState, snapshot) {\n      resolve = snapshot;\n    }\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const {rerender} = rtl.render(\n    <OwnTestComponent a={1}/>\n  );\n  rerender(\n    <OwnTestComponent a={1}/>\n  );\n\n  expect(resolve).toBe(true);\n  expect(updateInfos).toHaveLength(0);\n});\n\ntest('With implemented \"componentDidUpdate()\" with a snapshot', () => {\n  let resolve = false;\n  class OwnTestComponent extends React.Component {\n    static whyDidYouRender = true;\n    getSnapshotBeforeUpdate() {\n      return true;\n    }\n    componentDidUpdate(prevProps, prevState, snapshot) {\n      resolve = snapshot;\n    }\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const {rerender} = rtl.render(\n    <OwnTestComponent a={1}/>\n  );\n  rerender(\n    <OwnTestComponent a={1}/>\n  );\n\n  expect(resolve).toBe(true);\n  expect(updateInfos).toHaveLength(1);\n});\n\ntest('Component created with \"createReactClass\"', () => {\n  const CreateReactClassComponent = createReactClass({\n    displayName: 'Foo',\n    render() {\n      return <div>hi!</div>;\n    },\n  });\n\n  CreateReactClassComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <CreateReactClassComponent a={1}/>\n  );\n  rerender(\n    <CreateReactClassComponent a={2}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Component created with \"createReactClass\" with implemented \"componentDidUpdate()\"', () => {\n  let innerComponentDidUpdateCalled = false;\n  const CreateReactClassComponent = createReactClass({\n    displayName: 'Foo',\n    componentDidUpdate() {\n      innerComponentDidUpdateCalled = true;\n    },\n    render() {\n      return <div>hi!</div>;\n    },\n  });\n\n  CreateReactClassComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <CreateReactClassComponent a={1}/>\n  );\n  rerender(\n    <CreateReactClassComponent a={2}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n  expect(innerComponentDidUpdateCalled).toBe(true);\n});\n\ntest('Element created with \"createFactory\"', () => {\n  const TestComponentElementCreator = React.createFactory(TestComponent);\n\n  const {rerender} = rtl.render(\n    TestComponentElementCreator({a: 1})\n  );\n  rerender(\n    TestComponentElementCreator({a: 1})\n  );\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos).toHaveLength(1);\n});\n\ntest('Element created with \"cloneElement\"', () => {\n  const testElement = <TestComponent a={1}/>;\n  const testElement2 = React.cloneElement(testElement);\n\n  const {rerender} = rtl.render(testElement);\n  rerender(testElement2);\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Several class components', () => {\n  const {rerender} = rtl.render(\n    <>\n      <TestComponent/>\n      <TestComponent a={{a: 'a'}}/>\n      <TestComponent/>\n    </>\n  );\n\n  rerender(\n    <>\n      <TestComponent/>\n      <TestComponent a={{a: 'a'}}/>\n      <TestComponent/>\n    </>\n  );\n\n  expect(updateInfos).toHaveLength(3);\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: [{\n      diffType: diffTypes.deepEquals,\n      pathString: 'a',\n      nextValue: {a: 'a'},\n      prevValue: {a: 'a'},\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos[2].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n"
  },
  {
    "path": "tests/patches/patchForwardRefComponent.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('forward ref', () => {\n  const content = 'My component!!!';\n\n  const MyComponent = React.forwardRef((props, ref) => {\n    return <div ref={ref}>{content}</div>;\n  });\n\n  MyComponent.whyDidYouRender = true;\n\n  let componentContentFromRef = null;\n  let timesRefWasCalled = 0;\n\n  const handleRef = ref => {\n    if (!ref) {\n      return;\n    }\n    timesRefWasCalled++;\n    componentContentFromRef = ref.innerHTML;\n  };\n\n  const {rerender} = rtl.render(\n    <MyComponent a={[]} ref={handleRef}/>\n  );\n\n  rerender(\n    <MyComponent a={[]} ref={handleRef}/>\n  );\n\n  expect(componentContentFromRef).toBe(content);\n  expect(timesRefWasCalled).toBe(1);\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('forward ref a memo component', () => {\n  expect(() => {\n    const content = 'My component!!!';\n\n    const MyComponent = React.forwardRef(React.memo((props, ref) => {\n      return <div ref={ref}>{content}</div>;\n    }, () => true));\n\n    MyComponent.whyDidYouRender = true;\n\n    let componentContentFromRef = null;\n    let timesRefWasCalled = 0;\n\n    const handleRef = ref => {\n      if (!ref) {\n        return;\n      }\n      timesRefWasCalled++;\n      componentContentFromRef = ref.innerHTML;\n    };\n\n    const {rerender} = rtl.render(\n      <MyComponent a={[]} ref={handleRef}/>\n    );\n\n    rerender(\n      <MyComponent a={[]} ref={handleRef}/>\n    );\n\n    expect(componentContentFromRef).toBe(content);\n    expect(timesRefWasCalled).toBe(1);\n\n    expect(updateInfos).toHaveLength(1);\n    expect(updateInfos[0].reason).toEqual({\n      propsDifferences: [\n        {\n          pathString: 'a',\n          diffType: diffTypes.deepEquals,\n          prevValue: [],\n          nextValue: [],\n        },\n      ],\n      stateDifferences: false,\n      hookDifferences: false,\n      ownerDifferences: false,\n    });\n  }).toThrow();\n  global.flushConsoleOutput();\n});\n"
  },
  {
    "path": "tests/patches/patchFunctionalOrStrComponent.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nconst FunctionalTestComponent = () => (\n  <div>hi!</div>\n);\nFunctionalTestComponent.whyDidYouRender = true;\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('simple inline component', () => {\n  const InlineComponent = () => (\n    <div>hi!</div>\n  );\n  InlineComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <InlineComponent a={1}/>\n  );\n  rerender(\n    <InlineComponent a={2}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Several functional components', () => {\n  const {rerender} = rtl.render(\n    <>\n      <FunctionalTestComponent/>\n      <FunctionalTestComponent a={{a: 'a'}}/>\n      <FunctionalTestComponent/>\n    </>\n  );\n\n  rerender(\n    <>\n      <FunctionalTestComponent/>\n      <FunctionalTestComponent a={{a: 'a'}}/>\n      <FunctionalTestComponent/>\n    </>\n  );\n\n  expect(updateInfos).toHaveLength(3);\n\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: [{\n      diffType: diffTypes.deepEquals,\n      pathString: 'a',\n      nextValue: {a: 'a'},\n      prevValue: {a: 'a'},\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n\n  expect(updateInfos[2].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\n\n"
  },
  {
    "path": "tests/patches/patchMemoComponent.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport whyDidYouRender from '~';\nimport {diffTypes} from '~/consts';\n\nconst ReactMemoTestComponent = React.memo(() => (\n  <div>hi!</div>\n));\nReactMemoTestComponent.whyDidYouRender = true;\nReactMemoTestComponent.displayName = 'ReactMemoTestComponent';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('Memoize text component', () => {\n  const obj = {a: []};\n\n  const Svg = React.memo('svg');\n  Svg.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <Svg arr={obj}/>\n  );\n  rerender(\n    <Svg arr={obj}/>\n  );\n\n  expect(updateInfos).toHaveLength(0);\n});\n\ntest('Component memoized with React.memo - no change', () => {\n  const obj = {a: []};\n\n  const {rerender} = rtl.render(\n    <ReactMemoTestComponent arr={obj}/>\n  );\n  rerender(\n    <ReactMemoTestComponent arr={obj}/>\n  );\n\n  expect(updateInfos).toHaveLength(0);\n});\n\ntest('Component memoized with React.memo - different prop values', () => {\n  const {rerender} = rtl.render(\n    <ReactMemoTestComponent a={1}/>\n  );\n  rerender(\n    <ReactMemoTestComponent a={2}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.different,\n      prevValue: 1,\n      nextValue: 2,\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Component memoized with React.memo - deep equal prop values', () => {\n  const {rerender} = rtl.render(\n    <ReactMemoTestComponent a={[]}/>\n  );\n  rerender(\n    <ReactMemoTestComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('React.memo Component memoized with another React.memo - deep equal prop values', () => {\n  const ReactSecondMemoComponent = React.memo(ReactMemoTestComponent);\n  ReactSecondMemoComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <ReactSecondMemoComponent a={[]}/>\n  );\n  rerender(\n    <ReactSecondMemoComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      pathString: 'a',\n      diffType: diffTypes.deepEquals,\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('memo a forward ref component', () => {\n  const content = 'My component!!!';\n\n  const MyComponent = React.memo(React.forwardRef((props, ref) => {\n    return <div ref={ref}>{content}</div>;\n  }));\n\n  MyComponent.whyDidYouRender = true;\n\n  let componentContentFromRef = null;\n  let timesRefWasCalled = 0;\n\n  const handleRef = ref => {\n    if (!ref) {\n      return;\n    }\n    timesRefWasCalled++;\n    componentContentFromRef = ref.innerHTML;\n  };\n\n  const {rerender} = rtl.render(\n    <MyComponent a={[]} ref={handleRef}/>\n  );\n\n  rerender(\n    <MyComponent a={[]} ref={handleRef}/>\n  );\n\n  expect(componentContentFromRef).toBe(content);\n  expect(timesRefWasCalled).toBe(1);\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('memo a class component', () => {\n  class ClassComponent extends React.Component {\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const MyComponent = React.memo(ClassComponent);\n\n  MyComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <MyComponent a={[]}/>\n  );\n\n  rerender(\n    <MyComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('memo a pure class component', () => {\n  class ClassComponent extends React.PureComponent {\n    render() {\n      return <div>hi!</div>;\n    }\n  }\n\n  const MyComponent = React.memo(ClassComponent);\n\n  MyComponent.whyDidYouRender = true;\n\n  const {rerender} = rtl.render(\n    <MyComponent a={[]}/>\n  );\n\n  rerender(\n    <MyComponent a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n  global.flushConsoleOutput();\n});\n"
  },
  {
    "path": "tests/shouldTrack.test.js",
    "content": "import React from 'react';\n\nimport shouldTrack from '~/shouldTrack';\nimport whyDidYouRender from '~';\n\nclass TrackedTestComponent extends React.Component {\n  static whyDidYouRender = true;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nclass TrackedTestComponentNoHooksTracking extends React.Component {\n  static whyDidYouRender = {trackHooks: false};\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nclass NotTrackedTestComponent extends React.Component {\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nclass ExcludedTestComponent extends React.Component {\n  static whyDidYouRender = false;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nclass PureComponent extends React.PureComponent {\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nconst MemoComponent = React.memo(() => (\n  <div>hi!</div>\n));\nMemoComponent.displayName = 'MemoComponent';\n\ndescribe('shouldTrack', () => {\n  afterEach(() => {\n    React.__REVERT_WHY_DID_YOU_RENDER__();\n  });\n\n  describe('component changes', () => {\n    test('Do not track not tracked component (default)', () => {\n      whyDidYouRender(React);\n      const isShouldTrack = shouldTrack(NotTrackedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Track tracked component', () => {\n      whyDidYouRender(React);\n      const isShouldTrack = shouldTrack(TrackedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(true);\n    });\n\n    test('Track included not tracked components', () => {\n      whyDidYouRender(React, {\n        include: [/TestComponent/],\n      });\n      const isShouldTrack = shouldTrack(NotTrackedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(true);\n    });\n\n    test('Dont track components with whyDidYouRender=false', () => {\n      whyDidYouRender(React, {\n        include: [/ExcludedTestComponent/],\n      });\n      const isShouldTrack = shouldTrack(ExcludedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Do not track not included not tracked components', () => {\n      whyDidYouRender(React, {\n        include: [/0/],\n      });\n      const isShouldTrack = shouldTrack(NotTrackedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Do not track excluded tracked components', () => {\n      whyDidYouRender(React, {\n        exclude: [/TrackedTestComponent/],\n      });\n      const isShouldTrack = shouldTrack(TrackedTestComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Pure component', () => {\n      whyDidYouRender(React, {\n        trackAllPureComponents: true,\n      });\n      const isShouldTrack = shouldTrack(PureComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(true);\n    });\n\n    test('Memo component', () => {\n      whyDidYouRender(React, {\n        trackAllPureComponents: true,\n      });\n      const isShouldTrack = shouldTrack(MemoComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(true);\n    });\n\n    test('Pure component excluded', () => {\n      whyDidYouRender(React, {\n        trackAllPureComponents: true,\n        exclude: [/PureComponent/],\n      });\n      const isShouldTrack = shouldTrack(PureComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Memo component excluded', () => {\n      whyDidYouRender(React, {\n        trackAllPureComponents: true,\n        exclude: [/MemoComponent/],\n      });\n      const isShouldTrack = shouldTrack(MemoComponent, {isHookChange: false});\n      expect(isShouldTrack).toBe(false);\n    });\n  });\n\n  describe('hooks changes', () => {\n    test('Do not track not tracked component (default)', () => {\n      whyDidYouRender(React);\n      const isShouldTrack = shouldTrack(NotTrackedTestComponent, {isHookChange: true});\n      expect(isShouldTrack).toBe(false);\n    });\n\n    test('Track tracked component', () => {\n      whyDidYouRender(React);\n      const isShouldTrack = shouldTrack(TrackedTestComponent, {isHookChange: true});\n      expect(isShouldTrack).toBe(true);\n    });\n\n    test('Do not track hook changes with \"trackHooks: false\"', () => {\n      whyDidYouRender(React);\n      const isShouldTrack = shouldTrack(TrackedTestComponentNoHooksTracking, {isHookChange: true});\n      expect(isShouldTrack).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/strictMode.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport {diffTypes} from '~/consts';\nimport whyDidYouRender from '~';\n\nclass TestComponent extends React.Component {\n  static whyDidYouRender = true;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nclass PureTestComponent extends React.PureComponent {\n  static whyDidYouRender = true;\n  render() {\n    return <div>hi!</div>;\n  }\n}\n\nconst FunctionalTestComponent = () => (\n  <div>hi!</div>\n);\nFunctionalTestComponent.whyDidYouRender = true;\nFunctionalTestComponent.displayName = 'FunctionalTestComponent';\n\nconst FunctionalTestComponentWithHooks = () => {\n  const [state1, setState1] = React.useState({count1: 1});\n  const [state2, setState2] = React.useState({count2: 2});\n\n  React.useLayoutEffect(() => {\n    setState1({count1: 1});\n    setState2({count2: 2});\n  }, []);\n\n  return (\n    <div>hi! {state1.count1} {state2.count2}</div>\n  );\n};\nFunctionalTestComponentWithHooks.whyDidYouRender = true;\nFunctionalTestComponentWithHooks.displayName = 'FunctionalTestComponentWithHooks';\n\nconst ReactMemoTestComponent = React.memo(() => (\n  <div>hi!</div>\n));\nReactMemoTestComponent.whyDidYouRender = true;\nReactMemoTestComponent.displayName = 'ReactMemoTestComponent';\n\nlet updateInfos = [];\nbeforeEach(() => {\n  updateInfos = [];\n  whyDidYouRender(React, {\n    notifier: updateInfo => updateInfos.push(updateInfo),\n    logOwnerReasons: true,\n    trackHooks: true,\n  });\n});\n\nafterEach(() => {\n  React.__REVERT_WHY_DID_YOU_RENDER__();\n});\n\ntest('Strict mode- class component no props change', () => {\n  const {rerender} = rtl.render(\n    <React.StrictMode>\n      <div>\n        <TestComponent a={1}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  rerender(\n    <React.StrictMode>\n      <div>\n        <TestComponent a={1}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Strict mode- class component props change', () => {\n  const {rerender} = rtl.render(\n    <React.StrictMode>\n      <div>\n        <TestComponent a={[]}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  rerender(\n    <React.StrictMode>\n      <div>\n        <TestComponent a={[]}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Strict mode- pure class component no props change', () => {\n  const {rerender} = rtl.render(\n    <React.StrictMode>\n      <div>\n        <PureTestComponent a={1}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  rerender(\n    <React.StrictMode>\n      <div>\n        <PureTestComponent a={1}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  expect(updateInfos).toHaveLength(0);\n});\n\ntest('Strict mode- pure class component props change', () => {\n  const {rerender} = rtl.render(\n    <React.StrictMode>\n      <div>\n        <PureTestComponent a={[]}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  rerender(\n    <React.StrictMode>\n      <div>\n        <PureTestComponent a={[]}/>\n      </div>\n    </React.StrictMode>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [\n      {\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      },\n    ],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: false,\n  });\n});\n\ntest('Strict mode- functional component no props change', () => {\n  const Main = props => {\n    return (\n      <React.StrictMode>\n        <div>\n          <FunctionalTestComponent {...props}/>\n        </div>\n      </React.StrictMode>\n    );\n  };\n  const {rerender} = rtl.render(\n    <Main a={1}/>\n  );\n\n  rerender(\n    <Main a={1}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: {\n      hookDifferences: false,\n      propsDifferences: [],\n      stateDifferences: false,\n    },\n  });\n});\n\ntest('Strict mode- functional component with props change', () => {\n  const Main = props => {\n    return (\n      <React.StrictMode>\n        <div>\n          <FunctionalTestComponent {...props}/>\n        </div>\n      </React.StrictMode>\n    );\n  };\n  const {rerender} = rtl.render(\n    <Main a={[]}/>\n  );\n\n  rerender(\n    <Main a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(1);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: [{\n      diffType: diffTypes.deepEquals,\n      pathString: 'a',\n      prevValue: [],\n      nextValue: [],\n    }],\n    stateDifferences: false,\n    hookDifferences: false,\n    ownerDifferences: {\n      hookDifferences: false,\n      propsDifferences: [{\n        pathString: 'a',\n        diffType: diffTypes.deepEquals,\n        prevValue: [],\n        nextValue: [],\n      }],\n      stateDifferences: false,\n    },\n  });\n});\n\ntest('Strict mode- functional component with hooks no props change', () => {\n  const Main = props => {\n    return (\n      <React.StrictMode>\n        <div>\n          <FunctionalTestComponentWithHooks {...props}/>\n        </div>\n      </React.StrictMode>\n    );\n  };\n\n  rtl.render(\n    <Main a={1}/>\n  );\n\n  expect(updateInfos).toHaveLength(2);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: false,\n    hookDifferences: [\n      {\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {count1: 1},\n        prevValue: {count1: 1},\n      },\n    ],\n    ownerDifferences: false,\n  });\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: false,\n    hookDifferences: [\n      {\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {count2: 2},\n        prevValue: {count2: 2},\n      },\n    ],\n    ownerDifferences: false,\n  });\n});\n\ntest('Strict mode- functional component with hooks with props change', () => {\n  const Main = props => {\n    return (\n      <React.StrictMode>\n        <div>\n          <FunctionalTestComponentWithHooks {...props}/>\n        </div>\n      </React.StrictMode>\n    );\n  };\n\n  rtl.render(\n    <Main a={[]}/>\n  );\n\n  expect(updateInfos).toHaveLength(2);\n  expect(updateInfos[0].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: false,\n    hookDifferences: [\n      {\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {count1: 1},\n        prevValue: {count1: 1},\n      },\n    ],\n    ownerDifferences: false,\n  });\n  expect(updateInfos[1].reason).toEqual({\n    propsDifferences: false,\n    stateDifferences: false,\n    hookDifferences: [\n      {\n        diffType: diffTypes.deepEquals,\n        pathString: '',\n        nextValue: {count2: 2},\n        prevValue: {count2: 2},\n      },\n    ],\n    ownerDifferences: false,\n  });\n});\n\ntest('Strict mode- strict parent and child', () => {\n  const App = React.memo(() => {\n    const [whatever, setWhatever] = React.useState({a: 'b'});\n    const [whatever2, setWhatever2] = React.useState({a2: 'b2'});\n\n    const clickme = () => {\n      setWhatever({a: 'b'});\n      setWhatever2({a2: 'b2'});\n    };\n\n    return (\n      <div>\n        <button onClick={clickme}>test</button>\n        <div>{whatever.a} {whatever2.a2}</div>\n        <Child />\n      </div>\n    );\n  });\n\n  function Child() {\n    return <div>child</div>;\n  }\n\n  Child.whyDidYouRender = true;\n\n  const StrictApp = () => (\n    <React.StrictMode>\n      <App/>\n    </React.StrictMode>\n  );\n\n  const {getByText} = rtl.render(<StrictApp/>);\n  const buttonReference = getByText('test');\n\n  rtl.act(() => {\n    buttonReference.click();\n  });\n\n  rtl.act(() => {\n    buttonReference.click();\n  });\n\n  rtl.act(() => {\n    buttonReference.click();\n  });\n\n  const ownerDifferences = {\n    hookDifferences: [\n      {\n        hookName: 'useState',\n        differences: [\n          {\n            diffType: 'deepEquals',\n            pathString: '',\n            prevValue: {a: 'b'},\n            nextValue: {a: 'b'},\n          },\n        ],\n      },\n      {\n        hookName: 'useState',\n        differences: [\n          {\n            diffType: 'deepEquals',\n            pathString: '',\n            prevValue: {a2: 'b2'},\n            nextValue: {a2: 'b2'},\n          },\n        ],\n      },\n    ],\n    propsDifferences: false,\n    stateDifferences: false,\n  };\n\n  expect(updateInfos).toHaveLength(3);\n\n  expect(updateInfos[0].reason.ownerDifferences).toEqual(ownerDifferences);\n  expect(updateInfos[1].reason.ownerDifferences).toEqual(ownerDifferences);\n  expect(updateInfos[2].reason.ownerDifferences).toEqual(ownerDifferences);\n});\n"
  },
  {
    "path": "tests/utils.test.js",
    "content": "import React from 'react';\nimport * as rtl from '@testing-library/react';\n\nimport {checkIfInsideAStrictModeTree} from '~/utils';\n\ndescribe('checkIfInsideAStrictModeTree', () => {\n  test('class component', () => {\n    let isStrictMode;\n    class TestComponent extends React.Component {\n      static whyDidYouRender = true;\n      render() {\n        isStrictMode = checkIfInsideAStrictModeTree(this);\n        return <div>hi!</div>;\n      }\n    }\n\n    rtl.render(\n      <div>\n        <TestComponent/>\n      </div>\n    );\n\n    expect(isStrictMode).toBe(false);\n\n    rtl.render(\n      <React.StrictMode>\n        <>\n          <div>\n            <TestComponent/>\n          </div>\n          <div>\n            hiiiiiiiii\n          </div>\n        </>\n      </React.StrictMode>\n    );\n\n    expect(isStrictMode).toBe(true);\n  });\n\n  test('pure class component', () => {\n    let isStrictMode;\n    class TestComponent extends React.PureComponent {\n      static whyDidYouRender = true;\n      render() {\n        isStrictMode = checkIfInsideAStrictModeTree(this);\n        return <div>hi!</div>;\n      }\n    }\n\n    rtl.render(\n      <div>\n        <TestComponent/>\n      </div>\n    );\n\n    expect(isStrictMode).toBe(false);\n\n    rtl.render(\n      <React.StrictMode>\n        <>\n          <div>\n            <TestComponent/>\n          </div>\n          <div>\n            hiiiiiiiii\n          </div>\n        </>\n      </React.StrictMode>\n    );\n\n    expect(isStrictMode).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"include\": [\"**/*.js\", \"**/*.jsx\", \"**/*.ts\", \"**/*.tsx\", \"babel.config.cjs\", \"tests/babel.config.cjs\"],\n  \"exclude\": [\"node_modules\"],\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"allowJs\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"moduleResolution\": \"Node\",\n    \"jsx\": \"react\",\n    \"paths\": {\n      \"~*\": [\"src*\"],\n    }\n  },\n}\n"
  },
  {
    "path": "tsx-test.tsx",
    "content": "/* eslint-disable no-unused-vars */\nimport './types'\nimport React from 'react'\nimport * as Redux from 'react-redux'\nimport whyDidYouRender, { ExtraHookToTrack } from '.';\n\n/* SHOULD ERROR because bad trackExtraHooks was provided (second argument should be string) */\nwhyDidYouRender(React, {trackExtraHooks: [[Redux, Redux.useSelector]]});\nwhyDidYouRender(React, {trackExtraHooks: [[Redux, 'useSelector']]});\n\ninterface Props {\n  str: string\n}\n\nconst FunctionalComponent: React.FC<Props> = ({str}) => <div>{str}</div>\nFunctionalComponent.whyDidYouRender = true\nFunctionalComponent.whyDidYouRender = {collapseGroups: true}\n/* SHOULD ERROR because we use an unsupported whyDidYouRender prop */\nFunctionalComponent.whyDidYouRender = {nonWDYRProp: true}\n/* SHOULD ERROR because whyDidYouRender shouldn't be a string */\nFunctionalComponent.whyDidYouRender = 'a'\n\nconst MemoFunctionalComponent = React.memo<Props>(({str}) => <div>{str}</div>)\nMemoFunctionalComponent.whyDidYouRender = true\nMemoFunctionalComponent.whyDidYouRender = {collapseGroups: true}\n/* SHOULD ERROR because we use an unsupported whyDidYouRender prop */\nMemoFunctionalComponent.whyDidYouRender = {nonWDYRProp: true}\n/* SHOULD ERROR because whyDidYouRender shouldn't be a string */\nMemoFunctionalComponent.whyDidYouRender = 'a'\n\n/* SHOULD ERROR because bad trackExtraHooks was provided (second argument should be string) */\nFunctionalComponent.whyDidYouRender = {trackExtraHooks: [[Redux, Redux.useSelector]]}\nFunctionalComponent.whyDidYouRender = {trackExtraHooks: [[Redux, 'useSelector']]}\n\nclass RegularClassComponent extends React.Component<Props>{\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ClassComponentWithBooleanWDYR extends React.Component<Props>{\n  static whyDidYouRender = true\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ClassComponentWithObjWDYR extends React.Component<Props>{\n  static whyDidYouRender = {collapseGroups: true}\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ErroredClassComponentWithNonWDYRProp extends React.Component<Props>{\n  /* SHOULD ERROR because we use an unsupported whyDidYouRender prop */\n  static whyDidYouRender = {nonWDYRProp: 'a'}\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ErroredClassComponentWithStringWDYR extends React.Component<Props>{\n  /* SHOULD ERROR because whyDidYouRender shouldn't be a string */\n  static whyDidYouRender = 'a'\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ErrorousClassComponentWithTrackExtraHooks extends React.Component<Props>{\n  static whyDidYouRender = {\n    collapseGroups: true,\n    /* SHOULD ERROR because bad trackExtraHooks was provided (second argument should be string) */\n    trackExtraHooks: [[Redux, Redux.useSelector] as ExtraHookToTrack]\n  }\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ClassComponentWithTrackExtraHooks extends React.Component<Props>{\n  static whyDidYouRender = {\n    collapseGroups: true,\n    trackExtraHooks: [[Redux, 'useSelector'] as ExtraHookToTrack]\n  }\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass PureClassComponentWithBooleanWDYR extends React.PureComponent<Props>{\n  static whyDidYouRender = true\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass PureClassComponentWithObjWDYR extends React.PureComponent<Props>{\n  static whyDidYouRender = {collapseGroups: true}\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ErroredPureClassComponentWithNonWDYRProp extends React.PureComponent<Props>{\n  /* SHOULD ERROR because we use an unsupported whyDidYouRender prop */\n  static whyDidYouRender = {nonWDYRProp: 'a'}\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n\nclass ErroredPureClassComponentWithStringWDYR extends React.PureComponent<Props>{\n  /* SHOULD ERROR because whyDidYouRender shouldn't be a string */\n  static whyDidYouRender = 'a'\n  render(){\n    const {str} = this.props\n    return (\n      <div>{str}</div>\n    )\n  }\n}\n"
  },
  {
    "path": "types.d.ts",
    "content": "import * as React from 'react';\n\nexport interface HookDifference {\n  pathString: string;\n  diffType: string;\n  prevValue: any;\n  nextValue: any;\n}\n\nexport interface ReasonForUpdate {\n  hookDifferences: HookDifference[];\n  propsDifferences: boolean;\n  stateDifferences: boolean;\n}\n\nexport interface UpdateInfo {\n  Component: React.Component;\n  displayName: string;\n  prevProps: any;\n  prevState: any;\n  nextProps: any;\n  nextState: any;\n  prevHookResult: any;\n  nextHookResult: any;\n  reason: ReasonForUpdate;\n  options: WhyDidYouRenderOptions;\n  hookName?: string;\n}\n\nexport type ExtraHookToTrack = [any, string];\n\nexport interface WhyDidYouRenderOptions {\n  include?: RegExp[];\n  exclude?: RegExp[];\n  trackAllPureComponents?: boolean;\n  trackHooks?: boolean;\n  logOwnerReasons?: boolean;\n  trackExtraHooks?: Array<ExtraHookToTrack>;\n  logOnDifferentValues?: boolean;\n  hotReloadBufferMs?: number;\n  onlyLogs?: boolean;\n  collapseGroups?: boolean;\n  titleColor?: string;\n  diffNameColor?: string;\n  diffPathColor?: string;\n  textBackgroundColor?: string;\n  notifier?: Notifier;\n  customName?: string;\n}\n\nexport type WhyDidYouRenderComponentMember = WhyDidYouRenderOptions | boolean\n\nexport type Notifier = (options: UpdateInfo) => void\n\ndeclare function whyDidYouRender(react: typeof React, options?: WhyDidYouRenderOptions): typeof React;\n\ndeclare namespace whyDidYouRender {\n  export const defaultNotifier: Notifier;\n}\n\nexport default whyDidYouRender;\n\ndeclare module 'react' {\n  interface FunctionComponent<P = {}> {\n    whyDidYouRender?: WhyDidYouRenderComponentMember;\n  }\n\n  interface VoidFunctionComponent<P = {}> {\n    whyDidYouRender?: WhyDidYouRenderComponentMember;\n  }\n\n  interface ExoticComponent<P = {}> {\n    whyDidYouRender?: WhyDidYouRenderComponentMember;\n  }\n\n  namespace Component {\n    const whyDidYouRender: WhyDidYouRenderComponentMember;\n  }\n\n  /* not supported.\n  see https://github.com/microsoft/TypeScript/issues/33892\n  and https://github.com/microsoft/TypeScript/issues/34516\n  and https://github.com/microsoft/TypeScript/issues/32185\n\n  // interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {\n  //   static whyDidYouRender?: WhyDidYouRenderComponentMember;\n  // }\n  */\n}\n"
  }
]