Repository: gaearon/react-hot-loader Branch: master Commit: 07d1f4e080f1 Files: 323 Total size: 617.5 KB Directory structure: gitextract_mfr81t7r/ ├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PATRONS.md ├── README.md ├── babel.js ├── docs/ │ └── Troubleshooting.md ├── examples/ │ ├── SSR/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ ├── HiddenComponent.js │ │ │ ├── index.js │ │ │ ├── server.js │ │ │ └── template.js │ │ └── webpack.config.babel.js │ ├── all-possible-containers/ │ │ ├── .babelrc │ │ ├── .editorconfig │ │ ├── .flowconfig │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── App.js │ │ │ │ ├── ChildrenAsFunctionComponent.js │ │ │ │ ├── ChildrenAsFunctionExample.js │ │ │ │ ├── ClassComponent.js │ │ │ │ ├── ConnectedChildrenAFComponent.js │ │ │ │ ├── ConsumerClassComponent.js │ │ │ │ ├── ConsumerConnectedComponent.js │ │ │ │ ├── ConsumerFunctionComponent.js │ │ │ │ ├── ConsumerPureClassComponent.js │ │ │ │ ├── Counter.js │ │ │ │ ├── ErrorBoundary.js │ │ │ │ ├── FunctionComponent.js │ │ │ │ ├── FunctionConsumerPureClassComponent.js │ │ │ │ ├── Hook.js │ │ │ │ ├── LazyComponent.js │ │ │ │ ├── Modal.js │ │ │ │ ├── ModalComponent.js │ │ │ │ ├── PureClassComponent.js │ │ │ │ ├── SomethingWithHooks.js │ │ │ │ └── _editMe.js │ │ │ ├── context.js │ │ │ ├── index.js │ │ │ └── wcl.js │ │ └── webpack.config.js │ ├── async-components/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ ├── Counter2.js │ │ │ ├── chunks/ │ │ │ │ ├── async-component.js │ │ │ │ ├── loadable-components.js │ │ │ │ ├── react-imported-component.js │ │ │ │ ├── react-loadable.js │ │ │ │ └── react-universal-component.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── async-portals/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ ├── DeferredRender.js │ │ │ ├── HiddenComponent.js │ │ │ ├── Portal.js │ │ │ ├── async-component.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── decorators/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── electron-typescript/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app/ │ │ │ ├── App.tsx │ │ │ ├── Counter.tsx │ │ │ ├── development.html │ │ │ ├── index.tsx │ │ │ └── production.html │ │ ├── main.js │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── hotCold/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ ├── index.js │ │ │ └── reactHotLoader.setup.js │ │ └── webpack.config.babel.js │ ├── indeterminateComponent/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── mobx/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── multiple-hocs/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── parcel/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ └── src/ │ │ ├── App.js │ │ ├── Counter.js │ │ ├── Problem.js │ │ └── index.js │ ├── preact/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ ├── Internal.js │ │ │ ├── hotLoaderSetup.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── react-bootstrap/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── react-refetch/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Characters.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── redux/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── index_csp.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── HookComponent.js │ │ │ ├── MyComponent.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── styled-components/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── index_csp.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Async.js │ │ │ ├── Counter.js │ │ │ ├── Spring.js │ │ │ ├── config.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ ├── typescript/ │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── Counter.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── webpack.config.babel.js │ ├── typescript-no-babel/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── Counter.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── webpack/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.js │ │ │ ├── Counter.js │ │ │ └── index.js │ │ └── webpack.config.babel.js │ └── webpack-modern/ │ ├── .babelrc │ ├── .gitignore │ ├── package.json │ ├── src/ │ │ ├── App.js │ │ ├── Counter.js │ │ └── index.js │ └── webpack.config.babel.js ├── index.d.ts ├── index.js ├── package.json ├── patch.js ├── rollup.config.js ├── root.d.ts ├── root.js ├── scripts/ │ ├── ci.sh │ ├── react-15.ci.sh │ ├── react-16.ci.sh │ └── react-17.ci.sh ├── src/ │ ├── AppContainer.dev.js │ ├── AppContainer.prod.js │ ├── adapters/ │ │ └── preact.js │ ├── babel.dev.js │ ├── babel.prod.js │ ├── configuration.js │ ├── errorReporter.js │ ├── fresh/ │ │ └── babel.js │ ├── global/ │ │ ├── generation.js │ │ └── modules.js │ ├── hot.dev.js │ ├── hot.prod.js │ ├── index.dev.js │ ├── index.prod.js │ ├── internal/ │ │ ├── constants.js │ │ ├── getReactStack.js │ │ ├── reactUtils.js │ │ └── stack/ │ │ ├── hydrateFiberStack.js │ │ └── hydrateLegacyStack.js │ ├── logger.js │ ├── proxy/ │ │ ├── constants.js │ │ ├── createClassProxy.js │ │ ├── index.js │ │ ├── inject.js │ │ ├── transferStaticProps.js │ │ └── utils.js │ ├── reactHotLoader.js │ ├── reconciler/ │ │ ├── componentComparator.js │ │ ├── fiberUpdater.js │ │ ├── hotReplacementRender.js │ │ ├── index.js │ │ ├── proxies.js │ │ ├── proxyAdapter.js │ │ ├── resolver.js │ │ └── utils.js │ ├── utils/ │ │ └── runQueue.js │ ├── utils.dev.js │ ├── utils.prod.js │ └── webpack/ │ ├── index.js │ ├── makeIdentitySourceMap.js │ ├── patch.js │ └── webpackTagCommonJSExports.js ├── test/ │ ├── .eslintrc.js │ ├── AppContainer.dev.test.js │ ├── __babel_fixtures__/ │ │ ├── bindings.js │ │ ├── class-properties/ │ │ │ ├── arguments.js │ │ │ ├── arrow-function-in-constructor.js │ │ │ ├── async-functions-expression-body.js │ │ │ ├── async-functions.js │ │ │ ├── block-body.js │ │ │ ├── default-params.js │ │ │ ├── destructured-params.js │ │ │ ├── expression-body.js │ │ │ ├── nested-arguments.js │ │ │ ├── nested-new.target.js │ │ │ ├── new.target.js │ │ │ ├── not-a-function.js │ │ │ ├── not-an-arrow-function.js │ │ │ ├── same-name-as-class-method.js │ │ │ └── static-property.js │ │ ├── counter.js │ │ ├── drop-hot-half.prod.js │ │ ├── drop-hot.prod.js │ │ ├── hooks.js │ │ ├── issue-246.js │ │ ├── local-hooks.js │ │ └── name-clash.js │ ├── __snapshots__/ │ │ └── babel.test.js.snap │ ├── babel.test.js │ ├── build.test.js │ ├── cases/ │ │ ├── hooks/ │ │ │ └── useContext.test.js │ │ └── memo/ │ │ └── memo.test.js │ ├── global/ │ │ ├── generation.test.js │ │ └── modules.test.js │ ├── hot/ │ │ ├── .gitignore │ │ ├── createPatchedReact.js │ │ ├── react-dom.integration.spec.js │ │ └── react-dom.no-integration.spec.js │ ├── hot.dev.test.js │ ├── index.dev.test.js │ ├── internal/ │ │ ├── getReactStack.test.js │ │ └── reactUtils.test.js │ ├── prod/ │ │ ├── AppContainer.prod.test.js │ │ ├── hot.prod.test.js │ │ └── utils.prod.test.js │ ├── proxy/ │ │ ├── consistency.test.js │ │ ├── helper.js │ │ ├── instance-descriptor.test.js │ │ ├── instance-method.test.js │ │ ├── instance-property.test.js │ │ ├── lifecycle-method.test.js │ │ ├── static-descriptor.test.js │ │ ├── static-method.test.js │ │ ├── static-property.test.js │ │ └── unmounting.test.js │ ├── reactHotLoader.test.js │ ├── reconciler/ │ │ └── proxyAdapter.test.js │ ├── reconciler.test.js │ └── utils.test.js ├── testConfig/ │ ├── babel.js │ └── setupTests.js └── webpack.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ [ "env", { "modules": false, "loose": true } ], "react" ], "plugins": [ ["transform-class-properties", { "loose": true }], "transform-object-rest-spread", "dynamic-import-node" ] } ================================================ FILE: .eslintignore ================================================ node_modules/ __babel_fixtures__/ dist/ coverage/ examples/ babel.js index.js patch.js webpack.js root.js test/hot/react-dom ================================================ FILE: .eslintrc.js ================================================ module.exports = { extends: ['airbnb', 'prettier', 'prettier/react'], parser: 'babel-eslint', rules: { 'no-underscore-dangle': ['error', { allow: ['__standin_getCurrent'] }], 'no-console': ['error', { allow: ['warn', 'error'] }], 'no-plusplus': 'off', 'no-continue': 'off', 'no-restricted-syntax': 'off', 'no-prototype-builtins': 'off', 'no-param-reassign': 'off', 'no-constant-condition': 'off', 'no-shadow': 'off', 'class-methods-use-this': 'off', 'import/no-extraneous-dependencies': 'off', 'import/prefer-default-export': 'off', 'react/prop-types': 'off', 'react/prefer-stateless-function': 'off', 'react/no-multi-comp': 'off', 'react/prefer-es6-class': 'off', 'react/jsx-filename-extension': ['error', { extensions: ['.js'] }], 'react/require-default-props': 'off', 'jsx-a11y/no-static-element-interactions': 'off', 'jsx-a11y/click-events-have-key-events': 'off', }, globals: { __REACT_HOT_LOADER__: true, }, }; ================================================ FILE: .gitattributes ================================================ *.js text eol=lf ================================================ FILE: .gitignore ================================================ node_modules/ dist/ coverage/ .DS_Store ================================================ FILE: .prettierignore ================================================ __babel_fixtures__/ __snapshots__/ node_modules/ dist/ package.json CHANGELOG.md .cache babel.js index.js patch.js root.js test/hot/react-dom coverage ================================================ FILE: .prettierrc ================================================ { "singleQuote": true, "trailingComma": "all", "semi": true, "printWidth": 120 } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - 8 - 10 before_install: - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.3.2 - export PATH="$HOME/.yarn/bin:$PATH" script: - yarn ci notifications: email: false cache: yarn: true directories: - ".eslintcache" - "node_modules" ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": ["orta.vscode-jest", "editorconfig.editorconfig", "dbaeumer.vscode-eslint"] } ================================================ FILE: .vscode/settings.json ================================================ { "debug.node.autoAttach": "on" } ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ## [4.13.0](https://github.com/gaearon/react-hot-loader/compare/v4.12.20...v4.13.0) (2020-09-22) ### Bug Fixes * tailUpdate might be blocked by a PureComponent ([#1448](https://github.com/gaearon/react-hot-loader/issues/1448)) ([e44103a](https://github.com/gaearon/react-hot-loader/commit/e44103a)) * when module.hot is not truthy ([#1451](https://github.com/gaearon/react-hot-loader/issues/1451)) ([ec3447f](https://github.com/gaearon/react-hot-loader/commit/ec3447f)) ### Features * support React 17 ([#1557](https://github.com/gaearon/react-hot-loader/issues/1557)) ([c05396b](https://github.com/gaearon/react-hot-loader/commit/c05396b)), closes [#1425](https://github.com/gaearon/react-hot-loader/issues/1425) ### [4.12.21](https://github.com/gaearon/react-hot-loader/compare/v4.12.20...v4.12.21) (2020-04-30) ### Bug Fixes * add peerDependenciesMeta in package.json (#1433) ### [4.12.20](https://github.com/gaearon/react-hot-loader/compare/v4.12.19...v4.12.20) (2020-03-14) ### Bug Fixes * patch support for react-dom 16.13 ([#1428](https://github.com/gaearon/react-hot-loader/issues/1428)) ([5475fe7](https://github.com/gaearon/react-hot-loader/commit/5475fe7)) ### [4.12.19](https://github.com/gaearon/react-hot-loader/compare/v4.12.18...v4.12.19) (2020-01-22) ### Bug Fixes * don't record signatures of local hooks, fixes [#1412](https://github.com/gaearon/react-hot-loader/issues/1412) ([#1413](https://github.com/gaearon/react-hot-loader/issues/1413)) ([c159844](https://github.com/gaearon/react-hot-loader/commit/c159844)) ### [4.12.18](https://github.com/gaearon/react-hot-loader/compare/v4.12.17...v4.12.18) (2019-11-16) ### Bug Fixes * (regression) hook order change is causing React error, fixes [#1393](https://github.com/gaearon/react-hot-loader/issues/1393) ([6707b4b](https://github.com/gaearon/react-hot-loader/commit/6707b4b)) * production babel plugin might perform eager replacement, fixes [#1388](https://github.com/gaearon/react-hot-loader/issues/1388) ([b94adb3](https://github.com/gaearon/react-hot-loader/commit/b94adb3)) ### [4.12.17](https://github.com/gaearon/react-hot-loader/compare/v4.12.16...v4.12.17) (2019-11-12) ### Bug Fixes * move @types/react from to peer dependencies, fixes [#1391](https://github.com/gaearon/react-hot-loader/issues/1391) ([5ab2cab](https://github.com/gaearon/react-hot-loader/commit/5ab2cab)) ### [4.12.16](https://github.com/gaearon/react-hot-loader/compare/v4.12.15...v4.12.16) (2019-11-06) ### Bug Fixes * dont write to elementType if it is not defined, fixes [#1357](https://github.com/gaearon/react-hot-loader/issues/1357) ([1072303](https://github.com/gaearon/react-hot-loader/commit/1072303)) * explain why RHL could not be activated, fixes [#1362](https://github.com/gaearon/react-hot-loader/issues/1362) ([9561d66](https://github.com/gaearon/react-hot-loader/commit/9561d66)) * more @types/react to dependencies, fixes [#1359](https://github.com/gaearon/react-hot-loader/issues/1359) ([af42c1a](https://github.com/gaearon/react-hot-loader/commit/af42c1a)) * use logger.warn instead of console to report tail update, fixes [#1386](https://github.com/gaearon/react-hot-loader/issues/1386) ([fbc890a](https://github.com/gaearon/react-hot-loader/commit/fbc890a)) ## [4.12.15](https://github.com/gaearon/react-hot-loader/compare/v4.12.14...v4.12.15) (2019-10-08) ### Bug Fixes * React 16.10 support ([b7ed82a](https://github.com/gaearon/react-hot-loader/commit/b7ed82a)) ## [4.12.14](https://github.com/gaearon/react-hot-loader/compare/v4.12.13...v4.12.14) (2019-09-23) ### Bug Fixes * autodetect underupdated state and trigger an automatic update, fixes [#1342](https://github.com/gaearon/react-hot-loader/issues/1342) ([33c54f5](https://github.com/gaearon/react-hot-loader/commit/33c54f5)) * resolve all components to their last versions, [#1342](https://github.com/gaearon/react-hot-loader/issues/1342) ([62bc67e](https://github.com/gaearon/react-hot-loader/commit/62bc67e)) ## [4.12.13](https://github.com/gaearon/react-hot-loader/compare/v4.12.12...v4.12.13) (2019-09-12) ### Bug Fixes * error overlay should not be injected into the first instance, fixes [#1337](https://github.com/gaearon/react-hot-loader/issues/1337) ([c019663](https://github.com/gaearon/react-hot-loader/commit/c019663)) * return null for null types, fixes [#1324](https://github.com/gaearon/react-hot-loader/issues/1324) ([08d7ed1](https://github.com/gaearon/react-hot-loader/commit/08d7ed1)) * run hot in batched mode, fixes [#1332](https://github.com/gaearon/react-hot-loader/issues/1332) ([e801daf](https://github.com/gaearon/react-hot-loader/commit/e801daf)) ## [4.12.12](https://github.com/gaearon/react-hot-loader/compare/v4.12.11...v4.12.12) (2019-08-27) ### Bug Fixes * error overlay could fail by itself ([da50985](https://github.com/gaearon/react-hot-loader/commit/da50985)) * resolve undefined types to undefined, fixes [#1324](https://github.com/gaearon/react-hot-loader/issues/1324) ([73eeb4e](https://github.com/gaearon/react-hot-loader/commit/73eeb4e)) * speedup levenshtein comparison, fixes [#1087](https://github.com/gaearon/react-hot-loader/issues/1087) ([b81dcb0](https://github.com/gaearon/react-hot-loader/commit/b81dcb0)) ## [4.12.11](https://github.com/gaearon/react-hot-loader/compare/v4.12.10...v4.12.11) (2019-08-12) ### Features * react 16.9 support, [1320](https://github.com/gaearon/react-hot-loader/pull/1320) by @Bnaya ### Bug Fixes * add noRegister option to webpack, [#1315](https://github.com/gaearon/react-hot-loader/issues/1315) ([e562375](https://github.com/gaearon/react-hot-loader/commit/e562375)) * remove circular deps from hot-loader ([2cb544d](https://github.com/gaearon/react-hot-loader/commit/2cb544d)) ## [4.12.10](https://github.com/gaearon/react-hot-loader/compare/v4.12.9...v4.12.10) (2019-07-27) ### Bug Fixes * append react-hot-dom patch note to the ProxyFacade, fixes [#1311](https://github.com/gaearon/react-hot-loader/issues/1311) ([81bbb6a](https://github.com/gaearon/react-hot-loader/commit/81bbb6a)) * use void IIFE for webpack plugin, fixes [#1309](https://github.com/gaearon/react-hot-loader/issues/1309) ([6089822](https://github.com/gaearon/react-hot-loader/commit/6089822)) ## [4.12.9](https://github.com/gaearon/react-hot-loader/compare/v4.12.8...v4.12.9) (2019-07-23) ### Bug Fixes * forcely update Context Provider, fixes [#1207](https://github.com/gaearon/react-hot-loader/issues/1207) ([897a68d](https://github.com/gaearon/react-hot-loader/commit/897a68d)) * updating shallowequal dependency ([9269580](https://github.com/gaearon/react-hot-loader/commit/9269580)) ## [4.12.8](https://github.com/gaearon/react-hot-loader/compare/v4.12.7...v4.12.8) (2019-07-18) ### Bug Fixes * script error on IE11 due to lack of Object.entries ([6672b26](https://github.com/gaearon/react-hot-loader/commit/6672b26)) ## [4.12.7](https://github.com/gaearon/react-hot-loader/compare/v4.12.6...v4.12.7) (2019-07-16) ### Bug Fixes * false negative comparisons with react-hot-dom enabled, fixes [#1299](https://github.com/gaearon/react-hot-loader/issues/1299) ([a1c5c31](https://github.com/gaearon/react-hot-loader/commit/a1c5c31)) * reload hooks when hook body changes ([4795456](https://github.com/gaearon/react-hot-loader/commit/4795456)) ## [4.12.6](https://github.com/gaearon/react-hot-loader/compare/v4.12.5...v4.12.6) (2019-07-10) ### Bug Fixes * do not update hooks while updating hooks, fixes [#1294](https://github.com/gaearon/react-hot-loader/issues/1294) ([afa8ed4](https://github.com/gaearon/react-hot-loader/commit/afa8ed4)) ## [4.12.5](https://github.com/gaearon/react-hot-loader/compare/v4.12.4...v4.12.5) (2019-07-07) ### Bug Fixes * babel 7.5, fixes [#1292](https://github.com/gaearon/react-hot-loader/issues/1292) ([4441d6d](https://github.com/gaearon/react-hot-loader/commit/4441d6d)) ## [4.12.4](https://github.com/gaearon/react-hot-loader/compare/v4.12.3...v4.12.4) (2019-07-06) ### Bug Fixes * remove lodash, [#1269](https://github.com/gaearon/react-hot-loader/issues/1269) ([8ad1b46](https://github.com/gaearon/react-hot-loader/commit/8ad1b46)) ## [4.12.3](https://github.com/gaearon/react-hot-loader/compare/v4.12.2...v4.12.3) (2019-07-04) ### Bug Fixes * babel plugin should use only extrernals hooks, fixes [#1285](https://github.com/gaearon/react-hot-loader/issues/1285) ([c435eaa](https://github.com/gaearon/react-hot-loader/commit/c435eaa)) * make type comparison stronger ([1b9f2da](https://github.com/gaearon/react-hot-loader/commit/1b9f2da)) * prevent different typeof of components to be merged ([357249c](https://github.com/gaearon/react-hot-loader/commit/357249c)) * regression of registered type comparison, fixes [#1284](https://github.com/gaearon/react-hot-loader/issues/1284) ([49851be](https://github.com/gaearon/react-hot-loader/commit/49851be)) ## [4.12.2](https://github.com/gaearon/react-hot-loader/compare/v4.12.1...v4.12.2) (2019-07-03) ### Bug Fixes * [babel][prod] separate default and root 'hot' detection, fixes [#1283](https://github.com/gaearon/react-hot-loader/issues/1283) ([c6d29c9](https://github.com/gaearon/react-hot-loader/commit/c6d29c9)) ## [4.12.1](https://github.com/gaearon/react-hot-loader/compare/v4.12.0...v4.12.1) (2019-07-03) ### Bug Fixes * clone node for signature, fixes [#1268](https://github.com/gaearon/react-hot-loader/issues/1268) ([ed3e1d9](https://github.com/gaearon/react-hot-loader/commit/ed3e1d9)) * memo components are not updated ([5bca98c](https://github.com/gaearon/react-hot-loader/commit/5bca98c)) * use deep clone for fresh signature, fixes [#1280](https://github.com/gaearon/react-hot-loader/issues/1280) ([d796af8](https://github.com/gaearon/react-hot-loader/commit/d796af8)) # [4.12.0](https://github.com/gaearon/react-hot-loader/compare/v4.11.2...v4.12.0) (2019-06-30) ### Features * disable hot replacement render if react-dom is patched, see [#1274](https://github.com/gaearon/react-hot-loader/issues/1274) ([b93eb1d](https://github.com/gaearon/react-hot-loader/commit/b93eb1d)) ## [4.11.2](https://github.com/gaearon/react-hot-loader/compare/v4.11.1...v4.11.2) (2019-06-30) ### Bug Fixes * babel-plugin is not compatible with commonjs module output if babel 7 is used #1268. ## [4.11.1](https://github.com/gaearon/react-hot-loader/compare/v4.11.0...v4.11.1) (2019-06-15) ### Bug Fixes * hot render of forward props component ([4ba7530](https://github.com/gaearon/react-hot-loader/commit/4ba7530)) * regression in hidden components reconcilation ([3f8efc4](https://github.com/gaearon/react-hot-loader/commit/3f8efc4)) * update react-fresh babel plugin ([2fafd44](https://github.com/gaearon/react-hot-loader/commit/2fafd44)) # [4.11.0](https://github.com/gaearon/react-hot-loader/compare/v4.9.0...v4.11.0) (2019-06-10) ### Bug Fixes * source map consumer could be synchronous ([05a6c8e](https://github.com/gaearon/react-hot-loader/commit/05a6c8e)) ### Features * backport React.Fresh to handle hooks order change ([e5c4bb4](https://github.com/gaearon/react-hot-loader/commit/e5c4bb4)) # [4.10.0](https://github.com/gaearon/react-hot-loader/compare/v4.9.0...v4.10.0) (2019-06-02) ### Features * transparent class wrapping, fixes [#304](https://github.com/gaearon/react-hot-loader/issues/304) ([9fe4cad](https://github.com/gaearon/react-hot-loader/commit/9fe4cad)) # [4.9.0](https://github.com/gaearon/react-hot-loader/compare/v4.8.8...v4.9.0) (2019-06-02) ### Bug Fixes * false positive hot merge for forwardRef and Memo components, fixes [#1257](https://github.com/gaearon/react-hot-loader/issues/1257) ([dbf1047](https://github.com/gaearon/react-hot-loader/commit/dbf1047)) * improve hot api for production mode - error reporting and babel plugin ([6b29911](https://github.com/gaearon/react-hot-loader/commit/6b29911)) ### Features * support hooks update on HMR, fixes [#1256](https://github.com/gaearon/react-hot-loader/issues/1256) ([7ab076c](https://github.com/gaearon/react-hot-loader/commit/7ab076c)) ## [4.8.8](https://github.com/gaearon/react-hot-loader/compare/v4.8.7...v4.8.8) (2019-05-23) ### Bug Fixes * disable RHL when NODE_ENV == test, fixes [#1252](https://github.com/gaearon/react-hot-loader/issues/1252) ([6f56d87](https://github.com/gaearon/react-hot-loader/commit/6f56d87)) ## [4.8.7](https://github.com/gaearon/react-hot-loader/compare/v4.8.6...v4.8.7) (2019-05-20) ### Bug Fixes * remove checks. fixes [#1252](https://github.com/gaearon/react-hot-loader/issues/1252) ([2b2f733](https://github.com/gaearon/react-hot-loader/commit/2b2f733)) ## [4.8.6](https://github.com/gaearon/react-hot-loader/compare/v4.8.5...v4.8.6) (2019-05-18) ### Bug Fixes * No HMR missing error in SSR ([956e52b](https://github.com/gaearon/react-hot-loader/commit/956e52b)) * trailing commas at entrypoints, fixes [#1226](https://github.com/gaearon/react-hot-loader/issues/1226) ([989eebf](https://github.com/gaearon/react-hot-loader/commit/989eebf)) ## [4.8.5](https://github.com/gaearon/react-hot-loader/compare/v4.8.4...v4.8.5) (2019-05-16) ### Bug Fixes * correct memo fiber updater, fixes [#1230](https://github.com/gaearon/react-hot-loader/issues/1230) ([a185494](https://github.com/gaearon/react-hot-loader/commit/a185494)) * make API more error prone, fixes [#1228](https://github.com/gaearon/react-hot-loader/issues/1228) ([a6ba488](https://github.com/gaearon/react-hot-loader/commit/a6ba488)) * move react-dom notification to AppContainer, fixes [#1227](https://github.com/gaearon/react-hot-loader/issues/1227) ([28bfdd4](https://github.com/gaearon/react-hot-loader/commit/28bfdd4)) * update root endpoint, fixes [#1226](https://github.com/gaearon/react-hot-loader/issues/1226), [#1240](https://github.com/gaearon/react-hot-loader/issues/1240) ([608805a](https://github.com/gaearon/react-hot-loader/commit/608805a)) * use simpler conditions for index.js, implements [#1244](https://github.com/gaearon/react-hot-loader/issues/1244) ([4811d57](https://github.com/gaearon/react-hot-loader/commit/4811d57)) * webpack plugin fails on hot-loader/react-dom, fixes #[#1234](https://github.com/gaearon/react-hot-loader/issues/1234) ([826eee3](https://github.com/gaearon/react-hot-loader/commit/826eee3)) * webpack-plugin - use RHL global variable, fixes [#1251](https://github.com/gaearon/react-hot-loader/issues/1251) ([f036d36](https://github.com/gaearon/react-hot-loader/commit/f036d36)) ## [4.8.4](https://github.com/gaearon/react-hot-loader/compare/v4.8.3...v4.8.4) (2019-04-15) ### Bug Fixes * document the importance of import order, fixes [#1209](https://github.com/gaearon/react-hot-loader/issues/1209) ([5f50ee4](https://github.com/gaearon/react-hot-loader/commit/5f50ee4)) * dont skip first update or plain components ([432e0f8](https://github.com/gaearon/react-hot-loader/commit/432e0f8)) * remove Object.assign, fixes [#1226](https://github.com/gaearon/react-hot-loader/issues/1226) ([c5af009](https://github.com/gaearon/react-hot-loader/commit/c5af009)) ## [4.8.3](https://github.com/gaearon/react-hot-loader/compare/v4.8.2...v4.8.3) (2019-04-05) ### Bug Fixes * Allow refs on lazy components ([798e37f](https://github.com/gaearon/react-hot-loader/commit/798e37f)) * invariant violation react 15 ([1351f2d](https://github.com/gaearon/react-hot-loader/commit/1351f2d)) ## [4.8.1](https://github.com/gaearon/react-hot-loader/compare/v4.8.0...v4.8.1) (2019-03-29) ### Bug Fixes * root hot for parcel bundler, fixes [#1082](https://github.com/gaearon/react-hot-loader/issues/1082) ([a16679b](https://github.com/gaearon/react-hot-loader/commit/a16679b)) # [4.8.0](https://github.com/gaearon/react-hot-loader/compare/v4.7.2...v4.8.0) (2019-03-07) ### Features * support 16.8.4 ([38b63cd](https://github.com/gaearon/react-hot-loader/commit/38b63cd)) ## [4.7.2](https://github.com/gaearon/react-hot-loader/compare/v4.7.1...v4.7.2) (2019-03-04) ### Bug Fixes * safer reads from context consumer ([7942d26](https://github.com/gaearon/react-hot-loader/commit/7942d26)) * suppress error message on a server side, fixes [#1188](https://github.com/gaearon/react-hot-loader/issues/1188) ([b12b430](https://github.com/gaearon/react-hot-loader/commit/b12b430)) ## [4.7.1](https://github.com/gaearon/react-hot-loader/compare/v4.7.0...v4.7.1) (2019-02-20) ### Bug Fixes * destructed Context.Provider breaking registrations. fixes [#1184](https://github.com/gaearon/react-hot-loader/issues/1184) ([8240111](https://github.com/gaearon/react-hot-loader/commit/8240111)) * do not poison memoized props ([9f6ab6e](https://github.com/gaearon/react-hot-loader/commit/9f6ab6e)) * rethrow an error is hot comparison is not open ([8befa5a](https://github.com/gaearon/react-hot-loader/commit/8befa5a)) # [4.7.0](https://github.com/gaearon/react-hot-loader/compare/v4.6.5...v4.7.0) (2019-02-18) ### Bug Fixes * complain if hot patches not present, fixes [#1173](https://github.com/gaearon/react-hot-loader/issues/1173) ([efc3d6b](https://github.com/gaearon/react-hot-loader/commit/efc3d6b)) * deactivate RHL in dev mode if eval not allowed ([f995b0d](https://github.com/gaearon/react-hot-loader/commit/f995b0d)) * disable ErrorBoundaries for a first run, fixes [#1172](https://github.com/gaearon/react-hot-loader/issues/1172) ([2a834c2](https://github.com/gaearon/react-hot-loader/commit/2a834c2)) * Support Context.Provider reload and React.memo, fixes [#1169](https://github.com/gaearon/react-hot-loader/issues/1169) ([09e48eb](https://github.com/gaearon/react-hot-loader/commit/09e48eb)) ### Features * activate pureRender by default ([4e971b5](https://github.com/gaearon/react-hot-loader/commit/4e971b5)) * implement flexible hot injections ([b7e8f5e](https://github.com/gaearon/react-hot-loader/commit/b7e8f5e)) * make errors retryable to mitigate hooks update ([9967fde](https://github.com/gaearon/react-hot-loader/commit/9967fde)) ## [4.6.5](https://github.com/gaearon/react-hot-loader/compare/v4.6.4...v4.6.5) (2019-01-31) ### Bug Fixes * babel plugin produces a broken code ([6f8573f](https://github.com/gaearon/react-hot-loader/commit/6f8573f)) ## [4.6.4](https://github.com/gaearon/react-hot-loader/compare/v4.6.3...v4.6.4) (2019-01-31) ### Bug Fixes * do not shadow component name ([4b02767](https://github.com/gaearon/react-hot-loader/commit/4b02767)) * do not supress HMR errors ([be79d2f](https://github.com/gaearon/react-hot-loader/commit/be79d2f)) * fix wrong react-dom name resolution installation ([6f829a0](https://github.com/gaearon/react-hot-loader/commit/6f829a0)) * opt-out for module safety net, fixes [#1102](https://github.com/gaearon/react-hot-loader/issues/1102), [#1159](https://github.com/gaearon/react-hot-loader/issues/1159) ([93d0b1f](https://github.com/gaearon/react-hot-loader/commit/93d0b1f)) * remove platform checks from production bundle, fixes [#1162](https://github.com/gaearon/react-hot-loader/issues/1162) ([24d0448](https://github.com/gaearon/react-hot-loader/commit/24d0448)) * update fiber cached type, fixes [#1139](https://github.com/gaearon/react-hot-loader/issues/1139) ([35984ff](https://github.com/gaearon/react-hot-loader/commit/35984ff)) ## [4.6.3](https://github.com/gaearon/react-hot-loader/compare/v4.6.2...v4.6.3) (2018-12-19) ### Bug Fixes * context information is not always properly emulated in hot-render, fixes [#1094](https://github.com/gaearon/react-hot-loader/issues/1094) ([100fc9c](https://github.com/gaearon/react-hot-loader/commit/100fc9c)) * RHL could update non-relative components ([5d4f226](https://github.com/gaearon/react-hot-loader/commit/5d4f226)) * update memo updater and comparator. fix [#1135](https://github.com/gaearon/react-hot-loader/issues/1135) ([826a57c](https://github.com/gaearon/react-hot-loader/commit/826a57c)) ## [4.6.2](https://github.com/gaearon/react-hot-loader/compare/v4.6.1...v4.6.2) (2018-12-18) ### Bug Fixes * allow multiple 'hot' in one file, registering only the first one ([68c2a0a](https://github.com/gaearon/react-hot-loader/commit/68c2a0a)) * error overlay initialization prior body, fixes [#1127](https://github.com/gaearon/react-hot-loader/issues/1127) ([9177aba](https://github.com/gaearon/react-hot-loader/commit/9177aba)) ## [4.6.1](https://github.com/gaearon/react-hot-loader/compare/4.6.0...4.6.1) (2018-12-17) ### Bug Fixes * display hmr errors as hmr errors, [#1131](https://github.com/gaearon/react-hot-loader/issues/1131) ([615790f](https://github.com/gaearon/react-hot-loader/commit/615790f)) * error overlay is not server side friendly, [#1126](https://github.com/gaearon/react-hot-loader/issues/1126) ([40e3ff2](https://github.com/gaearon/react-hot-loader/commit/40e3ff2)) * hmr error detection is flaky ([9d3a2c0](https://github.com/gaearon/react-hot-loader/commit/9d3a2c0)) * secure wrapped/uwrapped comparison ([a62bacd](https://github.com/gaearon/react-hot-loader/commit/a62bacd)) * webpack plugin outputs es2015 code ([8a66401](https://github.com/gaearon/react-hot-loader/commit/8a66401)) # [4.6.0](https://github.com/gaearon/react-hot-loader/compare/v4.5.3...v4.6.0) (2018-12-13) ### Features * implement flexible error boundaries ([1846019](https://github.com/gaearon/react-hot-loader/commit/1846019)) * new root/hot for better error management. Fixes [#1078](https://github.com/gaearon/react-hot-loader/issues/1078), [#1111](https://github.com/gaearon/react-hot-loader/issues/1111) ([3029428](https://github.com/gaearon/react-hot-loader/commit/3029428)) ## [4.5.3](https://github.com/gaearon/react-hot-loader/compare/v4.5.2...v4.5.3) (2018-12-07) ### Bug Fixes * enable class equality, but disable class merge, when not hot ([8d214b3](https://github.com/gaearon/react-hot-loader/commit/8d214b3)) * react-dom hot-replacement is too active ([8827a40](https://github.com/gaearon/react-hot-loader/commit/8827a40)) ## [4.5.2](https://github.com/gaearon/react-hot-loader/compare/v4.5.1...v4.5.2) (2018-12-06) ### Bug Fixes * forwardRef to be remounted every frame. React-router to merge components ([3b11866](https://github.com/gaearon/react-hot-loader/commit/3b11866)) * React-router to merge components ([f45fee0](https://github.com/gaearon/react-hot-loader/commit/f45fee0)) * remove early reject, [#1115](https://github.com/gaearon/react-hot-loader/issues/1115) ([0a28144](https://github.com/gaearon/react-hot-loader/commit/0a28144)) ## [4.5.1](https://github.com/gaearon/react-hot-loader/compare/v4.5.0...v4.5.1) (2018-11-21) ### Bug Fixes * add lodash.merge as dep ([1de55d6](https://github.com/gaearon/react-hot-loader/commit/1de55d6)) # [4.5.0](https://github.com/gaearon/react-hot-loader/compare/v4.4.0...v4.5.0) (2018-11-20) ### Bug Fixes * handle older React versions ([a03c1c3](https://github.com/gaearon/react-hot-loader/commit/a03c1c3)) * hot-render forwardRef ([5f362ad](https://github.com/gaearon/react-hot-loader/commit/5f362ad)) * IE11 compact, [#1099](https://github.com/gaearon/react-hot-loader/issues/1099) ([f8ef550](https://github.com/gaearon/react-hot-loader/commit/f8ef550)) ### Features * webpack patch/inject mode ([42d637b](https://github.com/gaearon/react-hot-loader/commit/42d637b)) ## 4.4.0-1 (2018-11-01) ### Bug Fixes - `forwardRef` reconciliation [#1100](https://github.com/gaearon/react-hot-loader/pull/1100) ## [4.4.0](https://github.com/gaearon/react-hot-loader/compare/v4.3.11...v4.3.12) (2018-11-01) ### Features * React 16.5 and React 16.6 support. forwardRef, memo, lazy [#1084](https://github.com/gaearon/react-hot-loader/pull/1084) * Webpack loader [#1098](https://github.com/gaearon/react-hot-loader/pull/1098) * mark RHL sideEffect-free in production mode [#1096](https://github.com/gaearon/react-hot-loader/pull/1096) * babel plugin will remove `hot` in production [#1091](https://github.com/gaearon/react-hot-loader/pull/1091) ### Bug Fixes * babel plugin will remove calls to `hot` in production mode to remove side-effect on webpack [#1081](https://github.com/gaearon/react-hot-loader/pull/1081) * class methods, deleted on update, will be deleted from proxy [#1091](https://github.com/gaearon/react-hot-loader/pull/1091) ## [4.3.11](https://github.com/gaearon/react-hot-loader/compare/v4.3.9...v4.3.11) (2018-09-20) ### Bug Fixes * hot fixing ES5 literals in index.js ([80f6b63](https://github.com/gaearon/react-hot-loader/commit/80f6b63)) ## [4.3.10](https://github.com/gaearon/react-hot-loader/compare/v4.3.9...v4.3.10) (2018-09-20) ### Bug Fixes * IE10/CSP compatibility. [#1073](https://github.com/gaearon/react-hot-loader/pull/1073) ## [4.3.7](https://github.com/gaearon/react-hot-loader/compare/v4.3.6...v4.3.7) (2018-09-13) ### Bug Fixes * babel 7 compatibility. [#1043](https://github.com/gaearon/react-hot-loader/issues/1043) ([acad937](https://github.com/gaearon/react-hot-loader/commit/acad937)) ## [4.3.6](https://github.com/gaearon/react-hot-loader/compare/v4.3.5...v4.3.6) (2018-09-04) ### Bug Fixes * don't inadvertendly call getters ([322e746](https://github.com/gaearon/react-hot-loader/commit/322e746)) ## [4.3.5](https://github.com/gaearon/react-hot-loader/compare/4.3.4...4.3.5) (2018-08-23) ### Bug Fixes * dont hot-swap registered components, [#1050](https://github.com/gaearon/react-hot-loader/issues/1050) ([cf165a6](https://github.com/gaearon/react-hot-loader/commit/cf165a6)) * use the same conditions for index and patch ([f67d5b9](https://github.com/gaearon/react-hot-loader/commit/f67d5b9)) ## [4.3.4](https://github.com/gaearon/react-hot-loader/compare/v4.3.3...v4.3.4) (2018-07-25) ### Bug Fixes * element could be double-proxied ([#1033](https://github.com/gaearon/react-hot-loader/pull/1033)) * Components, not directly inherited from React.Components, like StyledComponents, are not working ([#1031](https://github.com/gaearon/react-hot-loader/pull/1031)) ## [4.3.3](https://github.com/gaearon/react-hot-loader/compare/4.3.1...4.3.3) (2018-06-15) ### Bug Fixes * add _this to sandbox variables, [#1020](https://github.com/gaearon/react-hot-loader/issues/1020) ([e5284ab](https://github.com/gaearon/react-hot-loader/commit/e5284ab)) ## [4.3.2](https://github.com/gaearon/react-hot-loader/compare/4.3.1...4.3.2) (2018-06-13) ### Bug Fixes * Add cold API to TypeScript definitions ## [4.3.1](https://github.com/gaearon/react-hot-loader/compare/4.3.0...4.3.1) (2018-06-09) ### Bug Fixes * Preact could pass arguments to the render, fix [#1013](https://github.com/gaearon/react-hot-loader/issues/1013) ([605da10](https://github.com/gaearon/react-hot-loader/commit/605da10)) * Support _this10 and over ([bb47ca4](https://github.com/gaearon/react-hot-loader/commit/bb47ca4)) * Handle lazy indeterminate static properties(Relay) [#1014](https://github.com/gaearon/react-hot-loader/pull/1014) # [4.3.0](https://github.com/gaearon/react-hot-loader/compare/v4.2.0...v4.3.0) (2018-06-05) ### Bug Fixes * Context Provider could crash due update, [#944](https://github.com/gaearon/react-hot-loader/issues/944) ([b0e2b5b](https://github.com/gaearon/react-hot-loader/commit/b0e2b5b)) * RHL babel plugin will ignore react and react-hot-loader, fixes [#900](https://github.com/gaearon/react-hot-loader/issues/900) ([e90a25c](https://github.com/gaearon/react-hot-loader/commit/e90a25c)) * RHL should add new class methods ([111d56e](https://github.com/gaearon/react-hot-loader/commit/111d56e)) * Multiple problems with methods update. Revert behavior back to v4.1.2 [#1001](https://github.com/gaearon/react-hot-loader/issues/1001) ### Features * Preact support [#952](https://github.com/gaearon/react-hot-loader/issues/952) ([2b40f57](https://github.com/gaearon/react-hot-loader/commit/2b40f57)) * Cold components [#991](https://github.com/gaearon/react-hot-loader/issues/991) ([9bcff36](https://github.com/gaearon/react-hot-loader/commit/9bcff36)) ## [4.2.0](https://github.com/gaearon/react-hot-loader/compare/v4.1.3...v4.2.0) (2018-05-16) ## Changes * Stateless Components will be converted to React.Component ones (as they were prior 4.1.0) [#977](https://github.com/gaearon/react-hot-loader/pull/977) ## Features * Basic support for React 16 Context [#979](https://github.com/gaearon/react-hot-loader/issues/979) ## Bug fixes * pure components wont update [#959](https://github.com/gaearon/react-hot-loader/issues/959), [#944](https://github.com/gaearon/react-hot-loader/issues/944) * better babel compliance ("this5"), [#969](https://github.com/gaearon/react-hot-loader/issues/969) * sideeffect-less updates [#970](https://github.com/gaearon/react-hot-loader/pull/970) * render as a class property [#924](https://github.com/gaearon/react-hot-loader/issues/924) * issues around reactLifecyclesCompat.polyfill [#951](https://github.com/gaearon/react-hot-loader/issues/951) * more examples and test cases * multiple reconsilation related bug fixes ## [4.1.3](https://github.com/gaearon/react-hot-loader/compare/v4.1.2...v4.1.3) (2018-05-08) ### Bug Fixes * always update bound functions. [#949](https://github.com/gaearon/react-hot-loader/issues/949) ([7819c71](https://github.com/gaearon/react-hot-loader/commit/7819c71)) * Fragment with a single element. fixes [#956](https://github.com/gaearon/react-hot-loader/issues/956) ([7e80881](https://github.com/gaearon/react-hot-loader/commit/7e80881)) * props merge order. [#967](https://github.com/gaearon/react-hot-loader/issues/967) ([#968](https://github.com/gaearon/react-hot-loader/issues/968)) ([1f8adb9](https://github.com/gaearon/react-hot-loader/commit/1f8adb9)) ## [4.1.2](https://github.com/gaearon/react-hot-loader/compare/4.1.0...4.1.2) (2018-04-24) ### Bug Fixes * condition render in Fragments [#942](https://github.com/gaearon/react-hot-loader/issues/942) ([#943](https://github.com/gaearon/react-hot-loader/issues/943)) ([2549a18](https://github.com/gaearon/react-hot-loader/commit/2549a18)) ## [4.1.1](https://github.com/gaearon/react-hot-loader/compare/4.1.0...4.1.1) (2018-04-24) ### Bug Fixes * Proxy should keep methods own props. [#918](https://github.com/gaearon/react-hot-loader/issues/918) ([a84dcd0](https://github.com/gaearon/react-hot-loader/commit/a84dcd0)) ## [4.1.0](https://github.com/gaearon/react-hot-loader/compare/4.0.1...4.1.0) (2018-04-18) ### Features * 🚀 React 16.3 support ([#918](https://github.com/gaearon/react-hot-loader/issues/918)) * 🧙🏻‍♂️ StatelessFunctionComponents are not wrapped by Stateful components anymore ([#873](https://github.com/gaearon/react-hot-loader/issues/873)) * 🧠Improved TypeScript support (no more than documentation) ([#884](https://github.com/gaearon/react-hot-loader/issues/884)) ### Bug Fixes * support babel temporal `_this3` ([#928](https://github.com/gaearon/react-hot-loader/issues/928)) ## [4.0.1](https://github.com/gaearon/react-hot-loader/compare/v4.0.0...v4.0.1) (2018-04-01) ### Bug Fixes * fix double proxy registration ([#915](https://github.com/gaearon/react-hot-loader/issues/915)) ([f8532df](https://github.com/gaearon/react-hot-loader/commit/f8532df)), closes [#912](https://github.com/gaearon/react-hot-loader/issues/912) * replace `.includes` by `.indexOf` (IE11 fix) ([#906](https://github.com/gaearon/react-hot-loader/issues/906)) ([87ad586](https://github.com/gaearon/react-hot-loader/commit/87ad586)) * break render recursion (MobX fix) ([#889](https://github.com/gaearon/react-hot-loader/issues/889)) ([33f2376](https://github.com/gaearon/react-hot-loader/commit/33f2376)) ### Docs * Improve TypeScript documentation ([#914](https://github.com/gaearon/react-hot-loader/issues/914)) ([d3b91de](https://github.com/gaearon/react-hot-loader/commit/d3b91de)) # [4.0.0](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-rc.0...v4.0.0) (2018-02-27) ### Bug Fixes * proper children reconcile for nested tags, fixes [#869](https://github.com/gaearon/react-hot-loader/issues/869) ([#871](https://github.com/gaearon/react-hot-loader/issues/871)) ([2de4e58](https://github.com/gaearon/react-hot-loader/commit/2de4e58)) # [4.0.0-rc.0](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.23...v4.0.0-rc.0) (2018-02-19) # [4.0.0-beta.23](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.22...v4.0.0-beta.23) (2018-02-18) ### Bug Fixes * disable RHL when HMR is not activated ([#863](https://github.com/gaearon/react-hot-loader/issues/863)) ([ffe0035](https://github.com/gaearon/react-hot-loader/commit/ffe0035)) * fix various bugs ([#857](https://github.com/gaearon/react-hot-loader/issues/857)) ([8fa1d42](https://github.com/gaearon/react-hot-loader/commit/8fa1d42)), closes [#845](https://github.com/gaearon/react-hot-loader/issues/845) [#843](https://github.com/gaearon/react-hot-loader/issues/843) * transfer original prototype methods ([#859](https://github.com/gaearon/react-hot-loader/issues/859)) ([0b7997f](https://github.com/gaearon/react-hot-loader/commit/0b7997f)), closes [#845](https://github.com/gaearon/react-hot-loader/issues/845) [#843](https://github.com/gaearon/react-hot-loader/issues/843) [#858](https://github.com/gaearon/react-hot-loader/issues/858) # [4.0.0-beta.22](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.21...v4.0.0-beta.22) (2018-02-10) ### Bug Fixes * fix reconciler warnings ([#852](https://github.com/gaearon/react-hot-loader/issues/852)) ([963677f](https://github.com/gaearon/react-hot-loader/commit/963677f)), closes [#843](https://github.com/gaearon/react-hot-loader/issues/843) ### Features * ship flat bundles ([#844](https://github.com/gaearon/react-hot-loader/issues/844)) ([7580552](https://github.com/gaearon/react-hot-loader/commit/7580552)) # [4.0.0-beta.21](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.20...v4.0.0-beta.21) (2018-02-05) ### Bug Fixes * fix proxy adapter ([#842](https://github.com/gaearon/react-hot-loader/issues/842)) ([9bb8251](https://github.com/gaearon/react-hot-loader/commit/9bb8251)) # [4.0.0-beta.20](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.19...v4.0.0-beta.20) (2018-02-04) # [4.0.0-beta.19](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.18...v4.0.0-beta.19) (2018-02-03) ### Bug Fixes * **areComponentsEqual:** fix behaviour ([#829](https://github.com/gaearon/react-hot-loader/issues/829)) ([d4dcd07](https://github.com/gaearon/react-hot-loader/commit/d4dcd07)) * **prop-types:** add prop-types as dependency ([#823](https://github.com/gaearon/react-hot-loader/issues/823)) ([c2b7c3c](https://github.com/gaearon/react-hot-loader/commit/c2b7c3c)) * regenerate overriden members ([#837](https://github.com/gaearon/react-hot-loader/issues/837)) ([39d4f5b](https://github.com/gaearon/react-hot-loader/commit/39d4f5b)), closes [#836](https://github.com/gaearon/react-hot-loader/issues/836) # [4.0.0-beta.18](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.17...v4.0.0-beta.18) (2018-01-25) ### Bug Fixes * break cyclic dependency ([#822](https://github.com/gaearon/react-hot-loader/issues/822)) ([328d793](https://github.com/gaearon/react-hot-loader/commit/328d793)), closes [#820](https://github.com/gaearon/react-hot-loader/issues/820) # [4.0.0-beta.17](https://github.com/gaearon/react-hot-loader/compare/4.0.0-beta.16...v4.0.0-beta.17) (2018-01-22) ### Features * remove useless dependencies ([e1b83e5](https://github.com/gaearon/react-hot-loader/commit/e1b83e5)), closes [#808](https://github.com/gaearon/react-hot-loader/issues/808) ### Bug Fixes * warn about errors [#814](https://github.com/gaearon/react-hot-loader/issues/814) * handle wrong module [#813](https://github.com/gaearon/react-hot-loader/issues/#813) # [4.0.0-beta.16](https://github.com/theKashey/react-hot-loader/compare/v4.0.0-beta.15-1...v4.0.0-beta.16) (2018-01-21) * **react-hot-loader:** Hard code consts from stand-in [#807](https://github.com/gaearon/react-hot-loader/issues/807)) * **react-hot-loader:** Support React 16 Fragments [#799](https://github.com/gaearon/react-hot-loader/issues/799)) * **react-hot-loader:** Suppress some warnings [#804](https://github.com/gaearon/react-hot-loader/issues/804)) * **react-hot-loader:** Better Electron support [#794](https://github.com/gaearon/react-hot-loader/issues/794)) * **react-stand-in:** Fix IE11 regression (again) # [4.0.0-beta.15](https://github.com/theKashey/react-hot-loader/compare/v4.0.0-beta.14...v4.0.0-beta.15) (2018-01-16) * **react-deep-force-update:** remove from the project * **react-stand-in:** fix MobX (Cannot assign to read only property 'render', [#796](https://github.com/gaearon/react-hot-loader/issues/796)) # [4.0.0-beta.14](https://github.com/theKashey/react-hot-loader/compare/v4.0.0-beta.13...v4.0.0-beta.14) (2018-01-14) * **react-hot-loader:** support IE11 ([#772](https://github.com/gaearon/react-hot-loader/issues/772)) * **react-stand-in:** support Relay Classis/Modern([#775](https://github.com/gaearon/react-hot-loader/issues/775)) # [4.0.0-beta.13](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.12...v4.0.0-beta.13) (2018-01-09) ### Features * **react-hot-loader:** detect wrong usage of `hot` ([#766](https://github.com/gaearon/react-hot-loader/issues/766)) ([b9738c7](https://github.com/gaearon/react-hot-loader/commit/b9738c7)), closes [#765](https://github.com/gaearon/react-hot-loader/issues/765) # [4.0.0-beta.12](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.11...v4.0.0-beta.12) (2018-01-02) ### Bug Fixes * target ES5 for production code ([b1d6d05](https://github.com/gaearon/react-hot-loader/commit/b1d6d05)), closes [#758](https://github.com/gaearon/react-hot-loader/issues/758) # [4.0.0-beta.11](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.10...v4.0.0-beta.11) (2017-12-30) ### Bug Fixes * **build:** fix builded files ([f4aa275](https://github.com/gaearon/react-hot-loader/commit/f4aa275)) # [4.0.0-beta.10](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.9...v4.0.0-beta.10) (2017-12-30) ### Bug Fixes * **babel:** do not use capital letters ([#754](https://github.com/gaearon/react-hot-loader/issues/754)) ([bf48675](https://github.com/gaearon/react-hot-loader/commit/bf48675)), closes [#753](https://github.com/gaearon/react-hot-loader/issues/753) # [4.0.0-beta.9](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.8...v4.0.0-beta.9) (2017-12-30) ### Bug Fixes * **react-hot-loader:** require `react-hot-loader/patch` in each file ([3038538](https://github.com/gaearon/react-hot-loader/commit/3038538)), closes [#750](https://github.com/gaearon/react-hot-loader/issues/750) # [4.0.0-beta.8](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.7...v4.0.0-beta.8) (2017-12-29) ### Bug Fixes * **react-hot-loader:** fix missing export ([239ca5d](https://github.com/gaearon/react-hot-loader/commit/239ca5d)) # [4.0.0-beta.7](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.6...v4.0.0-beta.7) (2017-12-29) ### Bug Fixes * handle async loading of patch ([#739](https://github.com/gaearon/react-hot-loader/issues/739)) ([af8bd4b](https://github.com/gaearon/react-hot-loader/commit/af8bd4b)) * **react-hot-loader:** fix componentWillUpdate ([95a9e79](https://github.com/gaearon/react-hot-loader/commit/95a9e79)) * use safe defineProperty ([f901192](https://github.com/gaearon/react-hot-loader/commit/f901192)) ### Features * replace warnings by `configure({ debug: true })` ([4f079c6](https://github.com/gaearon/react-hot-loader/commit/4f079c6)) # [4.0.0-beta.6](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-12-27) Same as 4.0.0-beta.5, fix build problem. # [4.0.0-beta.5](https://github.com/gaearon/react-hot-loader/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-12-27) ### Bug Fixes * improve decorators support (autobind) ([56883c9](https://github.com/gaearon/react-hot-loader/commit/56883c9)) * support nested children ([#735](https://github.com/gaearon/react-hot-loader/issues/735)) ([5c81655](https://github.com/gaearon/react-hot-loader/commit/5c81655)) # Manual changelog ### 4.0.0-beta.4 * Handle terminal node in stack (#728) ### 4.0.0-beta.3 * Use setTimeout to tick (#726) ### 4.0.0-beta.2 * Bunch of fixes (#725) ### 4.0.0-beta.1 * Remove webpack plugin (#707) * Replace react-proxy by react-stand-in (#707) * Replace react-deep-force-update by reconciler (#703) * Add `hot` HOC (#707) * Add `areComponentsEqual` (#304) * `warnings` flag now controls reconciler, not dev patch. ### 3.1.1 * Revert fix arrow function that uses props in constructor (#670) * Remove babel-template dependency (#671) ### 3.1.0 * Add an option to disable warnings (#669) * Fix arrow function that uses props in constructor (#670) ### 3.0.0 * Add React 16 support (#629, #658) * Remove RedBox as default error catcher (#494) ### 3.0.0-beta.6 * Use production versions of `patch` and `AppContainer` if no `module.hot` available, so it doesn't break people using `NODE_ENV=test`. (#398) * Opt out of transforming static class properties. (#381) ### 3.0.0-beta.5 * Makes the class properties portion of the Babel plugin work with async functions. (#372) * Change the output of the tagger code in the Babel plugin so that it doesn't break the output of `babel-node`. (#374) ### 3.0.0-beta.4 * Extends the Babel plugin to enable hot reloading of class properties. (#322) * Fixes a bug in the Webpack loader from a component importing a module with the same basename. (#347) ### 3.0.0-beta.3 * Fixes broken import of RedBox, which led to confusing stack traces when applications threw errors. (#314) * Add `module.hot` checks to conditional `require()`s to remove unnecessary warnings when using server rendering. (#302) ### 3.0.0-beta.2 * Patch `React.createFactory` (#287) * Fix props typo (#285) ### 3.0.0-beta.1 * Adds complete React Router support. Async routes should work fine now. (#272) * Fixes a nasty bug which caused unwrapped component to render. (#266, #272) * Fixes an issue that caused components with `shouldComponentUpdate` optimizations not getting redrawn (#269, 2a1e384d54e1919117f70f75dd20ad2490b1d9f5) * Internal: a rewrite and much better test coverage. ### 3.0.0-beta.0 * Fixes an issue when used in Webpack 2 (https://github.com/gaearon/react-hot-loader/issues/263) * **Breaking change:** instead of ```js ; ``` ```` you now need to write ```js ```` (#250) **See [this commit](https://github.com/gaearon/react-hot-boilerplate/commit/b52c727937a499f3efdc5dceb74ae952aa318c3a) as an update reference!** ### 3.0.0-alpha Big changes both to internals and usage. No docs yet but you can look at https://github.com/gaearon/react-hot-boilerplate/pull/61 for an example. ### 2.0.0-alpha **Experimental release that isn't really representative on what will go in 2.0, but uses the new engine.** Some ideas of what should be possible with the new engine: * There is no requirement to pass `getRootInstances()` anymore, so React Hot Loader doesn't need `react/lib/ReactMount` or walk the tree, which was somewhat fragile and changing between versions * Static methods and properties are now hot-reloaded * Instance getters and setters are now hot reloaded * Static getters and setters are now hot reloaded * Deleted instance methods are now deleted during hot reloading * Single method form of [autobind-decorator](https://github.com/andreypopp/autobind-decorator) is now supported What might get broken: * We no longer overwrite or even touch the original class. Every time makeHot is invoked, it will return a special proxy class. This means a caveat: for example, static methods will only be hot-reloaded if you refer to them as `this.constructor.doSomething()` instead of `FooBar.doSomething()`. This is because React Hot Loader calls `makeHot` right before exporting, so `FooBar` still refers to the original class. Similarly, `this.constructor === App` will be `false` inside `App` unless you call `App = makeHot(App)` manually, which you can't do with React Hot Loader. **I'm not sure how much of a problem this will be, so let me know if it pains you.** In the longer term, we will deprecate React Hot Loader in favor of a Babel plugin which will be able to rewrite class definitions correctly, so it shouldn't be a problem for a long time. If there is demand, we can introduce a mode that rewrites passed classes, too. ### 1.3.1 * Fix import for ReactMount to support 15.4.0 (**[#430](https://github.com/gaearon/react-hot-loader/pull/430)**) ### 1.3.0 * Recover from module errors on module level (**[#187](https://github.com/gaearon/react-hot-loader/pull/187)**) ### 1.2.9 * Silently ignore exports that raise an error when accessed (#114) * Update `source-map` dependency ### 1.2.8 * Remove React from peerDependencies * Update React Hot API to support React 0.14 beta 1 ### 1.2.7 * Preserve CommonJS `exports` context in the wrapping closure (**[#124](https://github.com/gaearon/react-hot-loader/issues/124)**) ### 1.2.6 * Fix autobinding on newly added methods for `createClass`-style classes ### 1.2.5 * Fix “React is not defined” error ### 1.2.4 * Avoid updating each class twice in React 0.12 ### 1.2.3 * Explicitly exclude `react/lib` files from processing. You **should** use `exclude: /node_modules/` in configuration, but at least this doesn't blow up for those who don't. ### 1.2.2 * Fix crash on React 0.13. Now compatible! ### 1.2.1 * Don't try to flatten inheritance chains, as it causes problems with `super` * Instead, automatically opt custom base classes into hot reloading as long as they descend from `React.Component` (in React 0.13). If your custom base class doesn't do that but you'd still want to have hot reloading, you need to manually opt it in via `module.makeHot` API. ### 1.2.0 * Support hot-reloading components without a base class (**[react-hot-api#5](https://github.com/gaearon/react-hot-api/issues/5)**) * Support hot-reloading inheritance chains (**[react-hot-api#10](https://github.com/gaearon/react-hot-api/issues/10)**) * Support using React 0.13 as an external ### 1.1.7 * Add React 0.13 RC2 to peerDeps ### 1.1.6 * Allow React 0.13 RC1 * Better support for ES6 inheritance * Fix reloading for modules with null prototype chain (**#82**) ### 1.1.5 * Wrap user code in IEFF to prevent losing `"use strict"`. Fixes #75 ### 1.1.4 * Fix crash when hot-reloading element factory. (Note: React discourages exporting factories.) ### 1.1.3 * Avoid warnings on React 0.13 ### 1.1.2 * Compatibility with React 0.13.0-beta.1 ### 1.1.1 * Fix edge cases by requiring `react/lib/ReactMount` in transformed source files * Add a warning if `ReactMount` doesn't return anything useful (e.g. when using external React) ### 1.1.0 * Skipping `node_modules` entirely [wasn't](https://github.com/gaearon/react-hot-loader/issues/58) [the best idea](https://github.com/gaearon/react-hot-loader/issues/55). Instead, we now specifically skip `node_modules/react/`, `node_modules/webpack/` and `node_modules/react-hot-loader/`. However you are still **encouraged** to [add `exclude: /node_modules/` to your loader config](https://github.com/gaearon/react-hot-boilerplate/blob/master/webpack.config.js#L24) for best performance. * Now modules that don't export any valid React classes in `module.exports` or any its properties will not be auto-accepted. This prevents hot loader from trying to handle non-React updates and allows changes in plain JS files to propagate to components that can handle them. For example, this allows [react-jss](https://github.com/jsstyles/react-jss) mixin to apply hot updates to JSS styles. ### 1.0.7 * Skip `node_modules` entirely. Fixes [#54](https://github.com/gaearon/react-hot-loader/issues/54) on Windows. ### 1.0.6 * Add `require('react-hot-loader/Injection')` to override Hot Loader behavior. Now you can supply your own way of getting root component instances, so Hot Loader can also work in environment where `require('react/lib/ReactMount')` is not available (for example, [when React is used as standalone bundle and not NPM package](https://github.com/gaearon/react-hot-loader/issues/53)). ### 1.0.5 * Fix stack overflow when hotifying same class twice ([#52](https://github.com/gaearon/react-hot-loader/issues/52)) ### 1.0.4 * Allow both `module.exports` and its properties be components (Fixes [#50](https://github.com/gaearon/react-hot-loader/issues/50)) ### 1.0.3 * In addition to hotifying `module.exports` by default, also hotify all its own properties ### 1.0.2 * Don't try to hot-replace `module.export`ed `ReactElement`s ### 1.0.1 * Delay `require`ing `ReactMount` to avoid circular dependencies * Don't process React or Webpack internals to avoid potential issues ### 1.0.0 * Don't rely on `createClass` regex or any other regex * Only `module.exports` is hot by default * Supports ES6 classes when they land in React 0.13 * Supports dynamically created classes * Manual mode See [what changed and how to migrate to 1.0](https://github.com/gaearon/react-hot-loader/blob/master/docs/README.md#migrating-to-10). ### 0.5.0 * Adds source map support, contributed by [Jake Riesterer](https://github.com/jRiest) ### 0.4.5 * Collapse all hot loader code in one line so it doesn't obscure beginning of file. ### 0.4.4 * Errors occuring in module definition (such as `ReferenceError`) should not disable further reloading (fixes **[#29](https://github.com/gaearon/react-hot-loader/issues/29)**) ### 0.4.3 * Support lowercase `react` reference name and usage with ES6 classes (`createClass(MyComponent.prototype)`) via **[#27](https://github.com/gaearon/react-hot-loader/issues/27)** ### 0.4.2 * Catch errors in modules and log them instead of reloading (fixes **[#21](https://github.com/gaearon/react-hot-loader/issues/21)**) ### 0.4.1 * Use more precise [`React.createClass` regex](https://github.com/gaearon/react-hot-loader/commit/f71c6785131adcc85b91789da0d0a0b9f1a9713f) to avoid matching own code when hot loader is applied to all JS files. ### 0.4.0 * Ignore files that contain no `createClass` calls (fixes **[#17](https://github.com/gaearon/react-hot-loader/issues/17)**) * Remove the need for pitch loader (fixes **[#19](https://github.com/gaearon/react-hot-loader/issues/19)**) * Improve performance by only using one loader instead of two * Now that performance is acceptable, remove desktop notifications and `notify` option * It is now recommended that you use `devtool: 'eval'` because it's much faster and has no downsides anymore ### 0.3.1 * Avoid warnings on old browsers with missing `Notification` API * Errors don't cause page reload anymore ### 0.3.0 * Use React 0.11 ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to `react-hot-loader` As the creators and maintainers of this project, we want to ensure that `react-hot-loader` lives and continues to grow and evolve. The evolution of the library should never be blocked by any single person's time. One of the simplest ways of doing this is by encouraging a larger set of shallow contributors. Through this, we hope to mitigate the problems of a project that needs updates but there's no-one who has the power to do so. ## Code contributions Here is a quick guide to doing code contributions to the library. 1. Find some issue you're interested in, or a feature that you'd like to tackle. Also make sure that no one else is already working on it. We don't want you to be disappointed. 2. Fork, then clone: `git clone https://github.com/YOUR_USERNAME/react-hot-loader.git` 3. Create a branch with a meaningful name for the issue: `git checkout -b fix-something` 4. Make your changes and commit: `git add` and `git commit` 5. Make sure that the tests still pass: `yarn test:es2015 --watch` and `yarn test:modern --watch` 6. Push your branch: `git push -u origin your-branch-name` 7. Submit a pull request to the upstream react-hot-loader repository. 8. Choose a descriptive title and describe your changes briefly. 9. Wait for a maintainer to review your PR, make changes if it's being recommended, and get it merged. 10. Perform a celebratory dance! :dancer: ### How do I set up the project? 1. First make sure you have [yarn](https://yarnpkg.com/) installed. 2. Run `yarn` and let `yarn dev` running in background, you are ready! 3. You can also run `yarn test:watch` to run tests in watch mode. ### How do I check if it really works? There is a lot of examples in the project, they are all under `examples/` folder. Choose an example to test and follow these steps: 1. Run `yarn` 2. Run `yarn install file:../packages/react-hot-loader` 3. Test it! ## Credits Heavily inspired from [styled-components contributing guidelines](https://github.com/styled-components/styled-components/blob/master/CONTRIBUTING.md). ================================================ FILE: ISSUE_TEMPLATE.md ================================================ If you are reporting a bug or having an issue setting up React Hot Loader, please fill in below. For feature requests, feel free to remove this template entirely. ### Description What you are reporting: ### Expected behavior What you think should happen: ### Actual behavior What actually happens: ### Environment React Hot Loader version: Run these commands in the project folder and fill in their results: 1. `node -v`: 2. `npm -v`: Then, specify: 1. Operating system: 2. Browser and version: ### Reproducible Demo Please take the time to create a new project that reproduces the issue. You can copy your project that experiences the problem and start removing things until you’re left with the minimal reproducible demo. This helps contributors, and you might get to the root of your problem during that process. Push to GitHub and paste the link here. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 Dan Abramov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: PATRONS.md ================================================ # Patrons The work on React Hot Loader, [React Transform](https://github.com/gaearon/babel-plugin-react-transform), [Redux](https://github.com/rackt/redux), and related projects was [funded by the community](https://www.patreon.com/reactdx). Meet some of the outstanding companies and individuals that made it possible: * [Webflow](https://github.com/webflow) * [Ximedes](https://www.ximedes.com/) * [Herman J. Radtke III](http://hermanradtke.com) * [Ken Wheeler](http://kenwheeler.github.io/) * [Chung Yen Li](https://www.facebook.com/prototocal.lee) * [Sunil Pai](https://twitter.com/threepointone) * [Charlie Cheever](https://twitter.com/ccheever) * [Eugene G](https://twitter.com/e1g) * [Matt Apperson](https://twitter.com/mattapperson) * [Jed Watson](https://twitter.com/jedwatson) * [Sasha Aickin](https://twitter.com/xander76) * [Stefan Tennigkeit](https://twitter.com/whobubble) * [Sam Vincent](https://twitter.com/samvincent) * Olegzandr Denman ================================================ FILE: README.md ================================================ # React Hot Loader [![Build Status][build-badge]][build] [![version][version-badge]][package] [![Code Coverage][coverage-badge]][coverage] [![MIT License][license-badge]][license] [![PRs Welcome][prs-badge]][prs] [![Chat][chat-badge]][chat] [![Backers on Open Collective][oc-backer-badge]](#backers) [![Sponsors on Open Collective][oc-sponsor-badge]](#sponsors) [![Watch on GitHub][github-watch-badge]][github-watch] [![Star on GitHub][github-star-badge]][github-star] Tweak React components in real time ⚛️⚡️ Watch **[Dan Abramov's talk on Hot Reloading with Time Travel](https://www.youtube.com/watch?v=xsSnOQynTHs).** # Moving towards next step React-Hot-Loader has been your friendly neighbour, living outside of React. But it has been limiting its powers and causing not the greatest experience. It's time to make a next step. **React-Hot-Loader is expected to be replaced by [React Fast Refresh](https://github.com/facebook/react/issues/16604)**. Please remove React-Hot-Loader if Fast Refresh is currently supported on your environment. * `React Native` - [supports Fast Refresh](https://facebook.github.io/react-native/docs/fast-refresh) since 0.61. * `parcel 2` - [supports Fast Refresh](https://github.com/facebook/react/issues/16604#issuecomment-556082893) since alpha 3. * `webpack` - [supports Fast Refresh](https://github.com/pmmmwh/react-refresh-webpack-plugin/) using a plugin. * `other bundler` - no support yet, use React-Hot-Loader * `create-react-app` - [supports Fast Refresh](https://create-react-app.dev/docs/advanced-configuration/) with FAST_REFRESH env since 4. * `next.js` - [supports Fast Refresh](https://nextjs.org/docs/basic-features/fast-refresh) since 9.4 ## Install ``` npm install react-hot-loader ``` > Note: You can safely install react-hot-loader as a regular dependency instead > of a dev dependency as it automatically ensures it is not executed in > production and the footprint is minimal. ## Getting started 1. Add `react-hot-loader/babel` to your `.babelrc`: ```js // .babelrc { "plugins": ["react-hot-loader/babel"] } ``` 2. Mark your root component as _hot-exported_: ```js // App.js import { hot } from 'react-hot-loader/root'; const App = () =>
Hello World!
; export default hot(App); ``` 3. Make sure `react-hot-loader` is required before `react` and `react-dom`: * or `import 'react-hot-loader'` in your main file (before React) * or prepend your webpack entry point with `react-hot-loader/patch`, for example: ```js // webpack.config.js module.exports = { entry: ['react-hot-loader/patch', './src'], // ... }; ``` 4. If you need hooks support, use [`@hot-loader/react-dom`](#hot-loaderreact-dom) ### Hook support Hooks would be auto updated on HMR if they _should_ be. There is only one condition for it - a non zero dependencies list. ```js ❄️ useState(initialState); // will never updated (preserve state) ❄️ useEffect(effect); // no need to update, updated on every render ❄️ useEffect(effect, []); // "on mount" hook. "Not changing the past" 🔥 useEffect(effect, [anyDep]); // would be updated 🔥 useEffect(effect, ["hot"]); // the simplest way to make hook reloadable ``` **Plus** * any hook would be reloaded on a function body change. Enabled by default, controlled by `reloadHooksOnBodyChange` option. * you may configure RHL to reload any hook by setting `reloadLifeCycleHooks` option to true. **To disable hooks reloading** - set configuration option: ```js import { setConfig } from 'react-hot-loader'; setConfig({ reloadHooks: false, }); ``` With this option set **all** `useEffects`, `useCallbacks` and `useMemo` would be updated on Hot Module Replacement. ### Hooks reset Hooks would be reset if their order changes. Adding, removing or moving around would cause a local tree remount. **Babel plugin is required** for this operation. Without it changing hook order would throw an error which would be propagated till the nearest class-based component. ## `@hot-loader/react-dom` [`@hot-loader/react-dom`](https://github.com/hot-loader/react-dom) replaces the "react-dom" package of the same version, but with additional patches to support hot reloading. There are 2 ways to install it: * Use **yarn** name resolution, so `@hot-loader/react-dom` would be installed instead of `react-dom` ``` yarn add react-dom@npm:@hot-loader/react-dom ``` * Use [webpack aliases](https://webpack.js.org/configuration/resolve/#resolvealias) ``` yarn add @hot-loader/react-dom ``` ```js // webpack.config.js module.exports = { // ... resolve: { alias: { 'react-dom': '@hot-loader/react-dom', }, }, }; ``` ### Old API **Note:** There is also an old version of `hot`, used prior to version 4.5.4. **Please use the new one**, as it is much more resilient to js errors that you may make during development. Meanwhile, not all the bundlers are compatible with new `/root` API, for example **[parcel](http://parceljs.org/) is not**. React-Hot-Load will throw an error, asking you to use the old API, if such incompatibility would be detected. It is almost the same, but you have to pass `module` inside `hot`. ```js import { hot } from 'react-hot-loader'; const App = () =>
Hello World!
; export default hot(module)(App); ``` 3. [Run webpack with Hot Module Replacement](https://webpack.js.org/guides/hot-module-replacement/#enabling-hmr): ```sh webpack-dev-server --hot ``` ## What about production? The webpack patch, `hot`, Babel plugin, `@hot-loader/react-dom` etc. are all safe to use in production; they leave a minimal footprint, so there is no need to complicate your configuration based on the environment. Using the Babel plugin in production is even recommended because it switches to cleanup mode. Just ensure that the production mode has been properly set, both as an environment variable and in your bundler. E.g. with webpack you would build your code by running something like: ``` NODE_ENV=production webpack --mode production ``` `NODE_ENV=production` is needed for the Babel plugin, while `--mode production` uses [`webpack.DefinePlugin`](https://webpack.js.org/plugins/define-plugin/) to set `process.env.NODE_ENV` inside the compiled code itself, which is used by `hot` and `@hot-loader/react-dom`. Make sure to watch your bundle size when implementing react-hot-loader to ensure that you did it correctly. ## Limitations * (that's the goal) React-Hot-Loader would not change the past, only update the present - no lifecycle event would be called on component update. As a result, any code changes made to `componentWillUnmount` or `componentDidMount` would be ignored for already created components. * (that's the goal) React-Hot-Loader would not update any object, including component `state`. * (1%) React-Hot-Loader may not apply some changes made to a component's `constructor`. Unless an existing component is recreated, RHL would typically _inject_ new data into that component, but there is no way to detect the actual change or the way it was applied, especially if the change was made to a function. This is because of the way React-Hot-Loader works - it knows what class functions are, not how they were created. See [#1001](https://github.com/gaearon/react-hot-loader/issues/1001) for details. ## Recipes ### Migrating from [create-react-app](https://github.com/facebookincubator/create-react-app) 1. Run `npm run eject` 2. Install React Hot Loader (`npm install --save-dev react-hot-loader`) 3. In `config/webpack.config.dev.js`, add `'react-hot-loader/babel'` to Babel loader configuration. The loader should now look like: ```js { test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, plugins: ['react-hot-loader/babel'], }, } ``` 4. Mark your App (`src/App.js`) as _hot-exported_: ```js // ./containers/App.js import React from 'react'; import { hot } from 'react-hot-loader'; const App = () =>
Hello World!
; export default hot(module)(App); ``` ### Migrating from [create-react-app](https://github.com/facebookincubator/create-react-app) without ejecting Users [report](https://github.com/gaearon/react-hot-loader/pull/729#issuecomment-354097936), that it is possible to use [react-app-rewire-hot-loader](https://github.com/cdharris/react-app-rewire-hot-loader) to setup React-hot-loader without ejecting. ### TypeScript As of version 4, React Hot Loader requires you to pass your code through [Babel](http://babeljs.io/) to transform it so that it can be hot-reloaded. This can be a pain point for TypeScript users, who usually do not need to integrate Babel as part of their build process. Fortunately, it's simpler than it may seem! Babel will happily parse TypeScript syntax and can act as an alternative to the TypeScript compiler, so you can safely replace `ts-loader` or `awesome-typescript-loader` in your Webpack configuration with `babel-loader`. Babel won't typecheck your code, but you can use [`fork-ts-checker-webpack-plugin`](https://github.com/Realytics/fork-ts-checker-webpack-plugin) (and/or invoke `tsc --noEmit`) as part of your build process instead. A sample configuration: ```js { // ...you'll probably need to configure the usual Webpack fields like "mode" and "entry", too. resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"] }, module: { rules: [ { test: /\.(j|t)sx?$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { cacheDirectory: true, babelrc: false, presets: [ [ "@babel/preset-env", { targets: { browsers: "last 2 versions" } } // or whatever your project requires ], "@babel/preset-typescript", "@babel/preset-react" ], plugins: [ // plugin-proposal-decorators is only needed if you're using experimental decorators in TypeScript ["@babel/plugin-proposal-decorators", { legacy: true }], ["@babel/plugin-proposal-class-properties", { loose: true }], "react-hot-loader/babel" ] } } } ] }, plugins: [ new ForkTsCheckerWebpackPlugin() ] }; ``` For a full example configuration of TypeScript with React Hot Loader and newest beta version of Babel, check [here](https://github.com/gaearon/react-hot-loader/tree/master/examples/typescript). As an alternative to this approach, it's possible to chain Webpack loaders so that your code passes through Babel and then TypeScript (or TypeScript and then Babel), but this approach is not recommended as it is more complex and may be significantly less performant. Read more [discussion here](https://github.com/gaearon/react-hot-loader/issues/884). ### Parcel Parcel supports Hot Module Reloading out of the box, just follow step 1 and 2 of [Getting Started](https://github.com/gaearon/react-hot-loader/tree/master#getting-started). We also have a [full example running Parcel + React Hot Loader](https://github.com/gaearon/react-hot-loader/tree/master/examples/parcel). ### Electron You need something to mark your modules as hot in order to use React Hot Loader. One way of doing this with Electron is to simply use webpack like any web-based project might do and the general guide above describes. See also [this example Electron app](https://github.com/s-h-a-d-o-w/rhl-electron-quick-start). A webpack-less way of doing it to use `electron-compile` (which is also used by [`electron-forge`](https://electronforge.io)) - see [this example](https://github.com/rllola/hmr-example-issue-2). While it requires less configuration, something to keep in mind is that `electron-compile`'s HMR will always reload all modules, regardless of what was actually edited. ### Source Maps If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. Source maps slow down your project. Use `devtool: 'eval'` for best build performance. Hot reloading code is just one line in the beginning and one line at the end of each module so you might not need source maps at all. ### Linking If you are using `npm link` or `yarn link` for development purposes, there is a chance you will get error `Module not found: Error: Cannot resolve module 'react-hot-loader'` or the linked package is not hot reloaded. There are 2 ways to fix `Module not found`: * Use [`include` in loader configuration](https://github.com/gaearon/react-hot-boilerplate/blob/master/webpack.config.js#L22) to only opt-in your app's files to processing. * Alternatively if you are using webpack, override the module resolution in your config: ```js { resolve: { alias: { 'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')), } } } ``` And to make your linked package to be hot reloaded, it will need to use the patched version of `react` and `react-dom`, if you're using webpack, add this options to the alias config ```js { resolve: { alias: { 'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')), // add these 2 lines below so linked package will reference the patched version of `react` and `react-dom` 'react': path.resolve(path.join(__dirname, './node_modules/react')), 'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom')), // or point react-dom above to './node_modules/@hot-loader/react-dom' if you are using it } } } ``` ## Preact React-hot-loader should work out of the box with `preact-compat`, but, in case of pure preact, you will need to configure it: * create configuration file (setupHotLoader.js) ```js import reactHotLoader from 'react-hot-loader'; import preact from 'preact'; reactHotLoader.preact(preact); ``` * dont forget to import it #### Preact limitations * HOCs and Decorators as not supported yet. For Preact React-Hot-Loader v4 behave as v3. ## React Native React Native **[supports hot reloading natively](https://facebook.github.io/react-native/blog/2016/03/24/introducing-hot-reloading.html)** as of version 0.22. Using React Hot Loader with React Native can cause unexpected issues (see #824) and is not recommended. ## Webpack plugin We recommend using the `babel` plugin, but there are some situations where you are unable to. If so, try the `webpack` plugin / `webpack-loader` (as seen in v3). Remember - the `webpack` plugin is **not compatible** with class-based components. The `babel` plugin will inject special methods to every class, to make `class members` (like onClick) hot-updatable, while the `webpack` plugin would leave classes as is, without any _instrumentation_. ```js class MyComponent extends React.Component { onClick = () => this.setState(); // COULD NOT UPDATE variable = 1; // this is ok render() {} // this is ok } ``` But `webpack-loader` could help with TypeScript or _spreading_ "cold API" [to all node_modules](https://github.com/gaearon/react-hot-loader#disabling-a-type-change-for-all-node_modules). > It is possible to enable this loader for all the files, but if you use `babel` plugin, you need to enable this loader for `react-dom` only. Place it after babel-loader, if babel-loader is present. ```js // webpack.config.js module.exports = { module: { rules: [ // would only land a "hot-patch" to react-dom { test: /\.js$/, include: /node_modules\/react-dom/, use: ['react-hot-loader/webpack'], }, ], }, }; ``` Webpack plugin will also land a "hot" patch to react-dom, making React-Hot-Loader more compliant to [the principles](https://github.com/gaearon/react-hot-loader/issues/1118). If you are not using `babel` plugin you might need to apply `webpack-loader` to all the files. ```js { test: /\.jsx?$/, include: /node_modules/, use: ['react-hot-loader/webpack'] }, ``` ### Code Splitting If you want to use Code Splitting + React Hot Loader, the simplest solution is to pick a library compatible with this one: * [React Lazy](https://reactjs.org/docs/code-splitting.html#reactlazy) * [Imported Component](https://github.com/theKashey/react-imported-component) * [React Universal Component](https://github.com/faceyspacey/react-universal-component) * [React-Loadable](https://github.com/jamiebuilds/react-loadable) If you use a not-yet-friendly library, like [react-async-component](https://github.com/ctrlplusb/react-async-component), or are having problems with hot reloading failing to reload code-split components, you can manually mark the components below the code-split boundaries. ```js // AsyncHello.js import { asyncComponent } from 'react-async-component'; // asyncComponent could not `hot-reload` itself. const AsyncHello = asyncComponent({ resolve: () => import('./Hello'), }); export default AsyncHello; ``` Note that `Hello` is the component at the root of this particular code-split chunk. ```js // Hello.js import { hot } from 'react-hot-loader/root'; const Hello = () => 'Hello'; export default hot(Hello); // <-- module will reload itself ``` Wrapping this root component with `hot()` will ensure that it is hot reloaded correctly. ### Out-of-bound warning You may see the following warning when code-split components are updated: ```console React-Hot-Loader: some components were updated out-of-bound. Updating your app to reconcile the changes. ``` This is because the hot reloading of code-split components happens asynchronously. If you had an `App.js` that implemented the `AsyncHello` component above and you modified `AsyncHello`, it would be bundled and reloaded at the same time as `App.js`. However, the core hot reloading logic is synchronous, meaning that it's possible for the hot reload to run before the updates to the split component have landed. In this case, RHL uses a special _tail update detection_ logic, where it notes that an an update to a split component has happened after the core hot reloading logic has already finished, and it triggers another update cycle to ensure that all changes are applied. The warning is informational - it is a notice that this tail update logic is triggered, and does not indicate a problem in the configuration or useage of `react-hot-loader`. If the tail update detection is not something you want or need, you can disable this behavior by setting `setConfig({ trackTailUpdates:false })`. ### Checking Element `type`s Because React Hot Loader creates proxied versions of your components, comparing reference types of elements won't work: ```js const element = ; console.log(element.type === Component); // false ``` React Hot Loader exposes a function `areComponentsEqual` to make it possible: ```js import { areComponentsEqual } from 'react-hot-loader'; const element = ; areComponentsEqual(element.type, Component); // true ``` Another way - compare "rendered" element type ```js const element = ; console.log(element.type === .type); // true // better - precache rendered type const element = ; const ComponentType = .type; console.log(element.type === ComponentType); // true ``` But you might have to provide all required props. See [original issue](https://github.com/gaearon/react-hot-loader/issues/304). This is most reliable way to compare components, but it will not work with required props. Another way - compare Component name. > Not all components have a name. **In production displayName could not exists.** ```js const element = ; console.log(element.displayName === 'Component'); // true ``` This is something we did not solve yet. Cold API could help keep original types. ### Webpack ExtractTextPlugin webpack ExtractTextPlugin is not compatible with React Hot Loader. Please disable it in development: ```js new ExtractTextPlugin({ filename: 'styles/[name].[contenthash].css', disable: NODE_ENV !== 'production', }); ``` #### Disabling a type change (❄️) It is possible to disable React-Hot-Loader for a specific component, especially to enable common way to type comparison. See #991 for the idea behind ⛄️, and #304 about "type comparison" problem. ```js import { cold } from 'react-hot-loader'; cold(SomeComponent) // this component will ignored by React-Hot-Loader .type === SomeComponent // true ``` If you will update `cold` component React-Hot-Loader will complain (on error level), and then React will cold-replace Component with a internal state lose. > Reach-Hot-Loader: cold element got updated ##### Disabling a type change for all node_modules You may _cold_ all components from node_modules. This will not work for HOC(like Redux) or dynamically created Components, but might help in most of situations, when type changes are not welcomed, and modules are not expected to change. ```js import { setConfig, cold } from 'react-hot-loader'; setConfig({ onComponentRegister: (type, name, file) => file.indexOf('node_modules') > 0 && cold(type), // some components are not visible as top level variables, // thus its not known where they were created onComponentCreate: (type, name) => name.indexOf('styled') > 0 && cold(type), }); ``` ! To be able to "cold" components from 'node_modules' you have to apply babel to node_modules, while this folder is usually excluded. You may add one more babel-loader, with only one React-Hot-Loader plugin inside to solve this. **Consider using webpack-loader** for this. ##### React-Hooks React hooks are not _really_ supported by React-Hot-Loader. Mostly due to our internal processes of re-rendering React Tree, which is required to reconcile an updated application before React will try to rerender it, and fail to do that, obviously. * hooks **should work** for versions 4.6.0 and above (`pureSFC` is enabled by default). * hooks will produce **errors** on every hot-update without patches to `react-dom`. * hooks **may loss the state** without patches to `react-dom`. * hooks does not support adding new hooks on the fly * change in hooks for a mounted components will cause a runtime exception, and a `retry` button (at the nearest class component) will be shown. Pressing a `retry` button will basically remount tree branch. To mitigate any hook-related issues (and disable their hot-reloadability) - `cold` them. * _cold_ components using hooks. ```js import { setConfig, cold } from 'react-hot-loader'; setConfig({ onComponentCreate: (type, name) => (String(type).indexOf('useState') > 0 || String(type).indexOf('useEffect') > 0) && cold(type), }); ``` ## API ### `hot(Component, options)` Mark a component as hot. #### Babel plugin Right now babel plugin has only one option, enabled by default. * `safetyNet` - will help you properly setup ReactHotLoader. You may disable it to get more control on the module execution order. ```js //.babelrc { "plugins": [ [ "react-hot-loader/babel", { "safetyNet": false } ] ] } ``` #### Important **!!** Use `hot` only for module `exports`, not for module `imports`. **!!** ```js import { hot } from 'react-hot-loader/root'; const App = () => 'Hello World!'; export default hot(App); ``` Keep in mind - by importing `react-hot-loader/root` you are setting up a boundary for update event propagation. The higher(in module hierarchy) you have it - the more stuff would be updated on Hot Module Replacement. To make RHL more reliable and safe, please place `hot` _below_ (ie somewhere in _imported_ modules): * react-dom * redux store creation * any data, you want to preserve between updates * big libraries You may(but it's not required) place `hot` to the every route/page/feature/lazy chunk, thus make updates more scoped. You don't need to wrap every component with `hot`, application works fine with a single one. ### (old)`hot(module, options)(Component, options)` Mark a component as hot. The "new" hot is just hidding the first part - `hot(module)`, giving you only the second `(App)`. The "new" hot is using old API. ```js import { hot } from 'react-hot-loader'; const App = () => 'Hello World!'; export default hot(module)(App); ``` ### `AppContainer` Mark application as hot reloadable. (**Prefer** using `hot` helper, see below for migration details). This low-level approach lets you make **hot **imports\_\_, not exports. ```js import React from 'react'; import ReactDOM from 'react-dom'; import { AppContainer } from 'react-hot-loader'; import App from './containers/App'; const render = Component => { ReactDOM.render( , document.getElementById('root'), ); }; render(App); // webpack Hot Module Replacement API if (module.hot) { // keep in mind - here you are configuring HMR to accept CHILDREN MODULE // while `hot` would configure HMR for the CURRENT module module.hot.accept('./containers/App', () => { // if you are using harmony modules ({modules:false}) render(App); // in all other cases - re-require App manually render(require('./containers/App')); }); } ``` ### areComponentsEqual(Component1, Component2) Test if two components have the same type. ```js import { areComponentsEqual } from 'react-hot-loader'; import Component1 from './Component1'; import Component2 from './Component2'; areComponentsEqual(Component1, Component2); // true or false ``` ### setConfig(config) Set a new configuration for React Hot Loader. Available options are: * `logLevel`: specify log level, default to `"error"`, available values are: `['debug', 'log', 'warn', 'error']` * `pureSFC`: enable Stateless Functional Component. If disabled they will be converted to React Components. Default value: false. * `ignoreSFC`: skip "patch" for SFC. "Hot loading" could still work, with webpack-patch present * `pureRender`: do not amend `render` method of any component. * for the rest see [index.d.ts](https://github.com/gaearon/react-hot-loader/blob/master/index.d.ts#L62-L133). ```js // rhlConfig.js import { setConfig } from 'react-hot-loader'; setConfig({ logLevel: 'debug' }); ``` **It is important** to set configuration before any other action will take a place ```js // index.js import './rhlConfig' // <-- extract configuration to a separate file, and import it in the beggining import React from 'react' .... ``` ## Migrating from v3 ### AppContainer vs hot Prior v4 the right way to setup React Hot Loader was to wrap your Application with `AppContainer`, set setup module acceptance by yourself. This approach is still valid but only for advanced use cases, prefer using `hot` helper. **React Hot Loader v3:** ```js // App.js import React from 'react'; const App = () =>
Hello world!
; export default App; ``` ```js // main.js import React from 'react'; import ReactDOM from 'react-dom'; import { AppContainer } from 'react-hot-loader'; import App from './containers/App'; const render = Component => { ReactDOM.render( , document.getElementById('root'), ); }; render(App); // webpack Hot Module Replacement API if (module.hot) { module.hot.accept('./containers/App', () => { // if you are using harmony modules ({modules:false}) render(App); // in all other cases - re-require App manually render(require('./containers/App')); }); } ``` **React Hot Loader v4:** ```js // App.js import React from 'react'; import { hot } from 'react-hot-loader'; const App = () =>
Hello world!
; export default hot(module)(App); ``` ```js // main.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './containers/App'; ReactDOM.render(, document.getElementById('root')); ``` ### Patch is optional > Since 4.0 till 4.8 Code is automatically patched, you can safely remove `react-hot-loader/patch` from your webpack config, if react-hot-loader is required before React in any other way. ### Error Boundary is inside every component > Since 4.5.4 On Hot Module Update we will inject `componentDidCatch` and a _special_ `render` to every Class-based component you have, making [Error Boundaries](https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries) more local. After update we will remove all sugar, keeping only Boundaries you've created. You can provide your own `errorReporter`, via `setConfig({errorReporter})` or opt-out from root ErrorBoundaries setting `errorBoundary={false}` prop on `AppContainer` or `hot`. However - this option affects only SFC behavior, and any ClassComponent would boundary itself. ```js import { setConfig } from 'react-hot-loader'; import ErrorBoundary from './ErrorBoundary'; // ErrorBoundary will be given error and errorInfo prop. setConfig({ errorReporter: ErrorBoundary }); ``` If `errorReporter` is not set - full screen error overlay would be shown. #### Setting global Error Reporter Global Error Reporter would, created a fixed overlay on top the page, would be used to display errors, not handled by `errorReporter`, and any HMR error. You may change, or disable this global error overlay ```js // to disable setConfig({ ErrorOverlay: () => null }); // to change setConfig({ ErrorOverlay: MyErrorOverlay }); ``` The UX of existing overlay is a subject to change, and we are open to any proposals. ## Known limitations and side effects ### Note about `hot` `hot` accepts only React Component (Stateful or Stateless), resulting the `HotExported` variant of it. The `hot` function will setup current module to _self-accept_ itself on reload, and will **ignore** all the changes, made for non-React components. You may mark as many modules as you want. But `HotExportedComponent` **should be the only used export** of a _hot_-module. > Note: Please note how often we have used `exported` keyword. `hot` is for exports. > Note: Does nothing in production mode, just passes App through. ### New Components keep executing the old code There is no way to hot-update constructor code, as result even new components will be born as the first ones, and then grow into the last ones. As of today, this issue cannot be solved. ## Troubleshooting If it doesn't work, in 99% of cases it's a configuration issue. A missing option, a wrong path or port. webpack is very strict about configuration, and the best way to find out what's wrong is to compare your project to an already working setup, check out **[examples](https://github.com/gaearon/react-hot-loader/tree/master/examples)**, bit by bit. If something doesn't work, in 99% of cases it's an issue with your code. The Component didn't get registered, due to HOC or Decorator around it, which is making it invisible to the Babel plugin or webpack loader. We're also gathering **[Troubleshooting Recipes](https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md)** so send a PR if you have a lesson to share! ### Switch into debug mode Debug mode adds additional warnings and can tells you why React Hot Loader is not working properly in your application. ```js import { setConfig } from 'react-hot-loader'; setConfig({ logLevel: 'debug' }); ``` ## Contributors This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md). [![contributors][oc-contributors-img]](https://github.com/gaearon/react-hot-loader/graphs/contributors) ## Backers Thank you to all our backers! 🙏 [Become a backer][oc-backer-link] [![backers][oc-backer-img]][oc-backer-link] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor][oc-sponsor-link] ## License MIT [build-badge]: https://img.shields.io/travis/gaearon/react-hot-loader.svg?style=flat-square [build]: https://travis-ci.org/gaearon/react-hot-loader [coverage-badge]: https://img.shields.io/codecov/c/github/gaearon/react-hot-loader.svg?style=flat-square [coverage]: https://codecov.io/github/gaearon/react-hot-loader [version-badge]: https://img.shields.io/npm/v/react-hot-loader.svg?style=flat-square [package]: https://www.npmjs.com/package/react-hot-loader [license-badge]: https://img.shields.io/npm/l/react-hot-loader.svg?style=flat-square [license]: https://github.com/gaearon/react-hot-loader/blob/next/LICENSE [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square [prs]: http://makeapullrequest.com [chat]: https://gitter.im/gaearon/react-hot-loader [chat-badge]: https://img.shields.io/gitter/room/gaearon/react-hot-loader.svg?style=flat-square [github-watch-badge]: https://img.shields.io/github/watchers/gaearon/react-hot-loader.svg?style=social [github-watch]: https://github.com/gaearon/react-hot-loader/watchers [github-star-badge]: https://img.shields.io/github/stars/gaearon/react-hot-loader.svg?style=social [github-star]: https://github.com/gaearon/react-hot-loader/stargazers [oc-backer-badge]: https://opencollective.com/react-hot-loader/backers/badge.svg [oc-sponsor-badge]: https://opencollective.com/react-hot-loader/sponsors/badge.svg [oc-contributors-img]: https://opencollective.com/react-hot-loader/contributors.svg?width=890&button=false [oc-backer-img]: https://opencollective.com/react-hot-loader/backers.svg?width=890 [oc-backer-link]: https://opencollective.com/react-hot-loader#backers [oc-sponsor-link]: https://opencollective.com/react-hot-loader#sponsor ================================================ FILE: babel.js ================================================ 'use strict'; if (process.env.NODE_ENV === 'production') { module.exports = require('./dist/babel.production.min.js'); } else { module.exports = require('./dist/babel.development.js'); } ================================================ FILE: docs/Troubleshooting.md ================================================ This file serves as a repository of common problems setting up React Hot Loader, and solutions to them. Know a problem? Feel free to send a PR with edits. ### What Should It Look Like? #### When the page loads ![](http://i.imgur.com/nuSa1i3.png) #### When you save a file ![](http://i.imgur.com/oOc0ikV.png) If you don't see some of the messages, or some of the requests, or if some of the requests fail, this may be a symptom of an incorrect configuration. Comparing your setup with [React Hot Boilerplate](https://github.com/gaearon/react-hot-boilerplate) may help you find the mistake. If hot loading appears to be working but you still do not see all of the above messages, you may need to check your dev server's [clientLogLevel](https://webpack.js.org/configuration/dev-server/#devserver-clientloglevel) setting. Depending on what it is set to, you may not see all of these messages, so you may need to adjust this setting to ensure that your configuration is correct. ##### Common TypeScript Mistake If you're a TypeScript user then to get set up with HMR then it's not unusual to alias `module` as an `any` like so: ```ts const anyModule = module as any; if (anyModule.hot) { anyModule.hot.accept('./app', () => render(App)); } ``` **DON'T DO THIS.** It will result in full page reloads each time you make a change. Instead do something like this: ```ts if ((module as any).hot) { (module as any).hot.accept('./app', () => render(App)); } ``` or this: ```ts declare const module: any; if (module.hot) { module.hot.accept('./app', () => render(App)); } ``` Then you should see the expected messages / get the expected behaviour: ``` [HMR] Updated modules: // ... [HMR] App is up to date. ``` --- ### Can't Build #### Cannot resolve 'file' or 'directory' `react/lib/ReactMount` If you're using a precompiled React instead of `react` npm package, React Hot Loader configuration will need a few tweaks. See [Usage with External React](https://github.com/gaearon/react-hot-loader/blob/master/docs/README.md#usage-with-external-react). Make sure you have `'.js'` in `resolve.extensions` section of Webpack config, or Webpack won't be able to find any JS files without explicitly specifying extension in `require`. #### SyntaxError: 'import' and 'export' may only appear at the top level If you're using React Hot Loader together with [Babel](https://babeljs.io/) (ex 6to5), make sure React Hot Loader stays **to the left** of Babel in `loaders` array in Webpack config: ```js { test: /\.jsx?$/, loaders: ['react-hot', 'babel'], include: path.join(__dirname, 'src') } ``` Webpack applies `loaders` right to left, and we need to feed Babel's _output_ to React Hot Loader, not vice versa. #### Error: Invalid path './' (or similar) If you're using a relative output path in your Webpack config, wrap it in a call to `path.resolve()`: ```js var path = require('path'); module.exports = { ..., output: { path: path.resolve('./my-relative-path'), ... } }; ``` If you used WebpackDevServer CLI mode and after switching to Node it crashes with `Error: Invalid path ''`, you probably didn't have `path` specified in `output` at all. You can just put `path: __dirname` there, as it won't matter for development config. ### Module not found: Error: Cannot resolve module 'react-hot-loader' Most likely you used `npm link` or `yarn link` to use a development version of a package in a different folder, and React Hot Loader processed it by mistake. Read the guide about linking package in [README linking section](../README.md#linking) --- ### Page Throws an Error #### Uncaught TypeError: Cannot read property 'NODE_ENV' of undefined #### Uncaught TypeError: Cannot read property 'env' of undefined #### [socket.io] Cannot use 'in' operator to search for 'document' in undefined Make sure you have `exclude: /node_modules/` or, better, `include: path.join(__dirname, 'src')` (path depends on your application) in loader configuration [just like on this line](https://github.com/gaearon/react-hot-boilerplate/blob/fbdbd93956241320bc3960d350c4dd0030cc6e84/webpack.config.js#L27). You never need to process `node_modules` with React Hot Loader. If you use other loaders such as `jsx?harmony` or `babel`, most likely they **also** need to have `include` specified. --- ### Can't Hot Reload Generally, the best way to fix this class of errors is to compare your setup to [React Hot Boilerplate](https://github.com/gaearon/react-hot-boilerplate) very carefully and see what's different. #### Try WebpackDevServer Node Interface Instead of CLI! WebpackDevServer CLI mode [behaves slightly differently](https://github.com/webpack/webpack-dev-server/issues/106) from its Node API. When in doubt, I suggest you use Node API like [React Hot Boilerplate does](https://github.com/gaearon/react-hot-boilerplate/blob/master/server.js). #### Check your NODE_ENV value If you are seeing an error like this: ``` [HMR] The following modules couldn't be hot updated: (Full reload needed) This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. ``` You may have `NODE_ENV` set to either `production` or `test`. Setting `NODE_ENV` to either of these will cause `react-hot-loader` to compile in production mode. Try setting `NODE_ENV` to something like `development`. #### Uncaught RangeError: Maximum call stack size exceeded When using WebpackDevServer CLI flag `--hot`, the plugin `new HotModuleReplacementPlugin()` should not be used and vice versa, they are mutually exclusive but the desired effect will work with any of them. #### No 'Access-Control-Allow-Origin' header is present on the requested resource. If you're trying to access Webpack Dev Server from a URL served on another port, you may try: * Changing `WebpackDevServer` options to include CORS header: ```js new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, hot: true, headers: { 'Access-Control-Allow-Origin': '*' }, }); ``` * Making sure that `webpack-dev-server` **client host and port** in `webpack.config.js` matches those of your development server: ```js entry: [ 'webpack-dev-server/client?http://localhost:3000', // WebpackDevServer host and port 'webpack/hot/only-dev-server', './src/app', ]; ``` #### The following modules couldn't be hot updated: (They would need a full reload!) **If you get this warning when editing a root component**, this may be because you don't export anything from it, and call `React.render` from there. Put your root component in a separate file (e.g. `App.jsx`) and `require` it from `index.js` where you call `React.render`. You also get this warning in v1.x if you write your root component as [stateless plain function](http://facebook.github.io/react/docs/reusable-components.html#stateless-functions) instead of using `React.Component`. This problem is already solved completely in the upcoming [v3.x](https://github.com/gaearon/react-hot-boilerplate/pull/61). This warning may also appear **if you edit some non-component file** which is `require`d from files other than components. This means hot update bubbled up, but the app couldn't handle it. This is normal! Just refresh. If you get this warning **together with a 404 for `hot-update.json` file**, you're probably using an ancient version of `webpack-dev-server` (just update it). #### I see “[WDS] Hot Module Replacement enabled” but nothing happens when I edit `App.js` If you're running Node 0.11.13, you might want to try updating to 0.12. Some people reported this helped solve this problem. Also **make sure that your `require`s have the same filename casing as the files.** Having `App.js` and doing `require('app')` might trip the watcher on some systems. OS X also has a rarely-occuring bug that causes some folders to get 'broken' with regards to file system change monitoring. Here are some suggested [fixes](http://feedback.livereload.com/knowledgebase/articles/86239). #### I see “[HMR] Nothing hot updated.” and nothing happens when I edit `App.js` If you have several entry points in `entry` configuration option, make sure `webpack/hot/only-dev-server` **is in each of them:** ```js entry: { app: ['./src/app', 'webpack/hot/only-dev-server'], editor: ['./src/editor', 'webpack/hot/only-dev-server'], ..., client: 'webpack-dev-server/client?http://localhost:3000' } ``` You will have to include "client.js" in your host page for the hot updates to work. For example: ```html ``` The entry points that don't have `webpack/hot/only-dev-server` (or `webpack/hot/dev-server` if you fancy occasional reloads) won't know how to apply hot updates. #### Syntax error: Unexpected token < If you combine WebpackDevServer with an existing server like Express and get this error message on hot updates, it is because Webpack is configured to request hot updates _from the current hostname_. So if your Express server is on `8000` and `publicPath` in Webpack config is `/build/`, it will request hot updates from `http://localhost:8000/build/`, which in your case is served by Express. Instead, you need to set `publicPath` to point to the port where WebpackDevServer is running. For example, it could be `http://localhost:9000/build/`. #### Not enough watchers Verify that if you have enough available watchers in your system. If this value is too low, the file watcher in Webpack won't recognize the changes: ``` cat /proc/sys/fs/inotify/max_user_watches ``` Arch users, add `fs.inotify.max_user_watches=524288` to `/etc/sysctl.d/99-sysctl.conf` and then execute `sysctl --system`. Ubuntu users (and possibly others): `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`. #### 404 errors for `hot-update.json` files First, make sure you have recent versions of Webpack and Webpack Dev Server (>= 1.7 is fine). Earlier versions use 404 code when no updates were available, so it wasn't technically an error. Now, take a look at the path where they are requested. Webpack uses `output.publicPath` from Webpack config to determine this path. If you forget to specify it, Webpack will request updates from a relative path to the current one, so any client-side routing will break it. Normally you want it to be `'/'` if you're serving scripts from root, something like `'/scripts/'` if you have a virtual path for scripts, and something like `'http://localhost:port/scripts/` if you're using Webpack only for scripts but have another primary server like Express. **This config variable must also match `publicPath` option specified when creating `WebpackDevServer` instance.** [Take a look at React Hot Boilerplate](https://github.com/gaearon/react-hot-boilerplate/blob/master/server.js#L6) to get an idea. --- ### Misc #### It's slowing down my build! Make sure you have `include` limited to your app's modules in loader configuration [just like on this line](https://github.com/gaearon/react-hot-boilerplate/blob/fbdbd93956241320bc3960d350c4dd0030cc6e84/webpack.config.js#L27). You never need to process `node_modules` with React Hot Loader. #### My bundle is so large! Make sure you have separate configs for development and production. You don't need `react-hot` in `loaders` or `webpack-dev-server/client` or `webpack/hot/only-dev-server` in production config. They are only for development. For easier maintenance, you can set an environment variable before invoking Webpack and read it in config. Also make sure you have these plugins in production config: ```js // removes a lot of debugging code in React new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production'), }, }), // keeps hashes consistent between compilations new webpack.optimize.OccurrenceOrderPlugin(), // minifies your code new webpack.optimize.UglifyJsPlugin({ compressor: { warnings: false, }, }); ``` Oh, and don't forget to remove `devtool: 'eval'` from a production config. Otherwise Uglify won't uglify anything at all. #### I can access my Single Page App (SPA) only via `/` on refresh The problem is that by default **WebpackDevServer** doesn't deal with HTML5 History correctly and the server won't route the url as it should. You can fix this issue by setting `historyApiFallback: true`. Here's a full example: ```js var webpack = require('webpack'); var WebpackDevServer = require('webpack-dev-server'); var config = require('./webpack.config'); var port = 4000; var ip = '0.0.0.0'; new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, historyApiFallback: true, }).listen(port, ip, function(err) { if (err) { return console.log(err); } console.log('Listening at ' + ip + ':' + port); }); ``` After this you should be able to access your SPA via any url that has been defined in it. #### React-hot-loader: a Unknown was found where a Unknown was expected. The problem is that after hot module update some branches of React Tree differs from the previous versions. As result React-hot-loader will not update these branches at all, and you may lose internal components state. The `equality` of Components are defined as: 1. They have same variable names in the same files. Ie they are both MyComponent from MyComponent.js 2. They have same displayName and similar code. > Note: similar code is not _equal_ code. RHL will stand some small changes in code, or even adding a new method in class. #### Not all of my code got updated. Hot module replacement is a tricky thing. Just double check that you are not exporting anything else from the modules with `hot` exported components – functions, constants, anything NOT REACT. `hot` function setups module self-acceptance. And it may be a dangerous thing. #### RHL is not working for Electron or Parcel `hot` function is not tested in these environments, yet. Please use `old school` AppContainer and setup HRM manually. #### RHL is not working with Webpack DLL React Hot Loader rely on a Babel transformation that register all exports in a global. That's why dependencies included in [Webpack DLL](https://webpack.js.org/plugins/dll-plugin/#dllplugin) will not work. #### React-hot-loader: fatal error caused by XXX - no instrumentation found. React-hot-loader found an Element without instrumentation due to a wrong configuration. To fix this issue - just require RHL before React. Example of a wrong configuration: ```js import * as React from 'react'; import { hot } from 'react-hot-loader'; // React is not patched ``` Example of correct configurations: ```js import { hot } from 'react-hot-loader'; import * as React from 'react'; // React is now patched ``` ```js import React from 'react'; import { hot } from 'react-hot-loader'; // React is now patched ``` ================================================ FILE: examples/SSR/.babelrc ================================================ { "presets": [ "env", "react" ], "plugins": [ "react-hot-loader/babel", "transform-class-properties", "dynamic-import-node" ] } ================================================ FILE: examples/SSR/.gitignore ================================================ node_modules ================================================ FILE: examples/SSR/package.json ================================================ { "name": "react-hot-loader-ssr", "version": "1.0.0", "license": "MIT", "scripts": { "build": "webpack", "start": "babel-node ./src/server.js", "start:webpack": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.9.7" }, "dependencies": { "babel-cli": "^6.26.0", "express": "^4.16.2", "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next", "react-portal": "^4.1.2" } } ================================================ FILE: examples/SSR/src/App.js ================================================ import { hot, setConfig } from 'react-hot-loader'; import * as React from 'react'; import Counter from './Counter'; // import hidden from './HiddenComponent'; const App = () => (

{40}!

{/*{hidden().counter}*/} SSR, and I work fine!

); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/SSR/src/Counter.js ================================================ import React from 'react'; const RAND = 1; //Math.round(Math.random() * 1000) class Counter extends React.Component { state = { count: 0 }; gen = 0; componentDidMount() { this.setState({ count: RAND, }); } componentWillUnmount() { clearInterval(this.interval); } render() { // gen should change. count - no. return ( {this.state.count}:{this.gen++} ); } } export default Counter; ================================================ FILE: examples/SSR/src/HiddenComponent.js ================================================ import React from 'react'; import Counter from './Counter'; const hidden = function() { return { counter: () => (
this is hidden counter()
), }; }; export default hidden; ================================================ FILE: examples/SSR/src/index.js ================================================ import 'babel-polyfill'; import React from 'react'; import { hydrate } from 'react-dom'; import App from './App'; //const root = document.createElement('div') //document.body.appendChild(root) const root = document.getElementById('root'); hydrate(, root); ================================================ FILE: examples/SSR/src/server.js ================================================ import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import App from './App'; import template from './template'; const server = express(); server.use('/dist', express.static('dist')); server.get('/', (req, res) => { const app = ; const appString = renderToString(app); res.send( template({ body: appString, }), ); }); server.listen(8080); console.log('server ready'); ================================================ FILE: examples/SSR/src/template.js ================================================ export default ({ body }) => { return `
${body}
`; }; ================================================ FILE: examples/SSR/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['webpack/hot/dev-server', './src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { //exclude: /node_modules|packages/, // should work without exclude test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin()], }; ================================================ FILE: examples/all-possible-containers/.babelrc ================================================ { "presets": [ "@babel/preset-flow", "@babel/preset-react", [ "@babel/preset-env", { "modules": false } ] ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-object-rest-spread", "@babel/plugin-syntax-dynamic-import" ], "env": { "test": { "plugins": [ "@babel/plugin-transform-modules-commonjs" ] }, "development": { "plugins": [ "react-hot-loader/babel" ] }, "production": { "plugins": [ "@babel/plugin-transform-react-constant-elements", "@babel/plugin-transform-react-inline-elements", "babel-plugin-transform-react-pure-class-to-function", "babel-plugin-transform-react-remove-prop-types" ] } } } ================================================ FILE: examples/all-possible-containers/.editorconfig ================================================ [*] charset=utf-8 end_of_line=crlf insert_final_newline=false indent_style=space indent_size=2 ================================================ FILE: examples/all-possible-containers/.flowconfig ================================================ [include] src/ [ignore] .*/__tests__/.* ================================================ FILE: examples/all-possible-containers/.gitignore ================================================ .idea dist node_modules ================================================ FILE: examples/all-possible-containers/LICENSE ================================================ MIT License Copyright (c) 2017 Eli Sherer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: examples/all-possible-containers/README.md ================================================ # react-hot-loader-repro918 Reproduce an issue for gaearon/react-hot-loader#918 ================================================ FILE: examples/all-possible-containers/package.json ================================================ { "name": "react-hot-loader-repro918", "version": "1.0.0", "license": "UNLICENSED", "description": "Reproduce an issue for gaearon/react-hot-loader#918", "repository": { "type": "git", "url": "https://github.com/elisherer/react-hot-loader-repro918" }, "author": { "name": "Eli Sherer", "email": "eli.sherer@gmail.com", "url": "http://www.sherer.co.il" }, "private": true, "main": "src/index.js", "scripts": { "build": "cross-env NODE_ENV=production webpack -p", "start": "webpack-serve --port 8080 --host 0.0.0.0", "start:link": "cp -R ../../dist ./node_modules/react-hot-loader && cp -R ../../*.js ./node_modules/react-hot-loader" }, "dependencies": { "react": "^16.8.6", "react-dom": "^16.8.6" }, "devDependencies": { "@babel/core": "^7.0.0", "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0", "@babel/plugin-transform-react-constant-elements": "^7.0.0", "@babel/plugin-transform-react-inline-elements": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", "babel-core": "^7.0.0-bridge.0", "babel-loader": "8.0.0", "babel-plugin-transform-react-pure-class-to-function": "1.0.1", "babel-plugin-transform-react-remove-prop-types": "0.4.13", "cross-env": "5.1.4", "html-webpack-plugin": "3.2.0", "jsx-compress-loader": "^0.0.1", "react-hot-loader": "4.3.10", "uglifyjs-webpack-plugin": "^2.0.1", "webpack": "4.6.0", "webpack-cli": "2.0.15", "webpack-serve": "0.3.2" } } ================================================ FILE: examples/all-possible-containers/src/components/App.js ================================================ // @flow import { hot } from 'react-hot-loader/root'; import React from 'react'; import Context from '../context'; import Counter from './Counter'; import ErrorBoundary from './ErrorBoundary'; import ModalComponent from './ModalComponent'; import ClassComponent from './ClassComponent'; import FunctionComponent from './FunctionComponent'; import PureClassComponent from './PureClassComponent'; import ConsumerClassComponent from './ConsumerClassComponent'; import ConsumerFunctionComponent from './ConsumerFunctionComponent'; import ConsumerPureClassComponent from './ConsumerPureClassComponent'; import ChildrenAsFunctionExample from './ChildrenAsFunctionExample'; import ConsumerConnectedComponent from './ConsumerConnectedComponent'; import ConnectedChildrenAFComponent from './ConnectedChildrenAFComponent'; import FunctionConsumerPureClassComponent from './FunctionConsumerPureClassComponent'; import HookedComponent from './Hook'; import { EDIT_ME } from './_editMe'; const Secret = (() => { const A = () => (
component A
); const B = () => 'wrong'; return { A, B }; })(); class App extends React.Component { state = { error: null, errorInfo: null, open: false, }; render() { const { open, error, errorInfo } = this.state; const { A, B } = Secret; return (
App Content {EDIT_ME}
{open && this.setState({ open: false })} />}
{value => (value === '42' ? : )}
); } } let ExportedApp = App; if (__DEV__) { const { setConfig } = require('react-hot-loader'); setConfig({ logLevel: 'debug', errorReporter: ErrorBoundary, }); ExportedApp = hot(App); } export default ExportedApp; ================================================ FILE: examples/all-possible-containers/src/components/ChildrenAsFunctionComponent.js ================================================ import React from 'react'; import { EDIT_ME } from './_editMe'; class ChildrenAsFunctionComponent extends React.Component { render() { return
{this.props.children('passed-argument')}
; } } export default ChildrenAsFunctionComponent; ================================================ FILE: examples/all-possible-containers/src/components/ChildrenAsFunctionExample.js ================================================ import React from 'react'; import ChildrenAsFunctionComponent from './ChildrenAsFunctionComponent'; import { EDIT_ME } from './_editMe'; const ChildrenAsFunctionExample = () => ( {value => (
Children as a function Component (value={value}) {EDIT_ME}
)}
); export default ChildrenAsFunctionExample; ================================================ FILE: examples/all-possible-containers/src/components/ClassComponent.js ================================================ import React from 'react'; import { EDIT_ME } from './_editMe'; class ClassComponent extends React.Component { render() { return (
Class Component {EDIT_ME}
); } } export default ClassComponent; ================================================ FILE: examples/all-possible-containers/src/components/ConnectedChildrenAFComponent.js ================================================ import React from 'react'; import ChildrenAsFunctionComponent from './ChildrenAsFunctionComponent'; import { connect } from '../context'; import { EDIT_ME } from './_editMe'; const ConnectedChildrenAFComponent = ({ consumedValue }) => ( {value => (
Children as a function Component (value={value}, consumedValue={consumedValue}) {EDIT_ME}
)}
); export default connect(ConnectedChildrenAFComponent); ================================================ FILE: examples/all-possible-containers/src/components/ConsumerClassComponent.js ================================================ import React from 'react'; import Context from '../context'; import { EDIT_ME } from './_editMe'; class ConsumerClassComponent extends React.Component { render() { return ( {value => (
Consumer Class Component (value={value}) {EDIT_ME}
)}
); } } export default ConsumerClassComponent; ================================================ FILE: examples/all-possible-containers/src/components/ConsumerConnectedComponent.js ================================================ import React from 'react'; import { connect } from '../context'; import { EDIT_ME } from './_editMe'; class ConsumerConnectedComponent extends React.Component { render() { return (
Consumer connected Component (value={this.props.consumedValue}) {EDIT_ME}
); } } export default connect(ConsumerConnectedComponent); ================================================ FILE: examples/all-possible-containers/src/components/ConsumerFunctionComponent.js ================================================ import React from 'react'; import Context from '../context'; import { EDIT_ME } from './_editMe'; const ConsumerFunctionComponent = () => ( {value => (
Consumer Function Component (value={value}) {EDIT_ME}
)}
); export default ConsumerFunctionComponent; ================================================ FILE: examples/all-possible-containers/src/components/ConsumerPureClassComponent.js ================================================ import React from 'react'; import Context from '../context'; import { EDIT_ME } from './_editMe'; class ConsumerPureClassComponent extends React.Component { render() { return ( {value => (
Consumer Pure Class Component (value={value}) {EDIT_ME}
)}
); } } export default ConsumerPureClassComponent; ================================================ FILE: examples/all-possible-containers/src/components/Counter.js ================================================ import React from 'react'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return
#{this.state.count}
; } } export default Counter; ================================================ FILE: examples/all-possible-containers/src/components/ErrorBoundary.js ================================================ // @flow import React from 'react'; const ErrorBoundary = ({ error, errorInfo }: { error: any, errorInfo: Object }) => (

{'Oh-no! Something went wrong'}

{error && error.toString()}

Stacktrace:
{errorInfo && errorInfo.componentStack && errorInfo.componentStack.split('\n').map((line, i) =>
{line}
)}
); export default ErrorBoundary; ================================================ FILE: examples/all-possible-containers/src/components/FunctionComponent.js ================================================ import React from 'react'; import { EDIT_ME } from './_editMe'; const FunctionComponent = () => (
Function Component {EDIT_ME}
); export default FunctionComponent; ================================================ FILE: examples/all-possible-containers/src/components/FunctionConsumerPureClassComponent.js ================================================ import React from 'react'; import Context from '../context'; import { EDIT_ME } from './_editMe'; /** * The bug is reproduced in this file, edit the content of PureTest and see it doesn't update * (Changing the PureComponent to Component makes the HMR work) */ class PureTest extends React.PureComponent { render() { return (
Function Consumer Pure Class Component (value={this.props.value}) {EDIT_ME}
); } } const FunctionConsumerPureClassComponent = props => ( {value => ( {value => } )} ); export default FunctionConsumerPureClassComponent; ================================================ FILE: examples/all-possible-containers/src/components/Hook.js ================================================ import React, { useState, useEffect } from 'react'; import { hot } from 'react-hot-loader/root'; const SomeComponent = ac(() => import('./Counter')); const App = () => (
Hook with Async context
); export default hot(App); // function ac(importComponent) { return function Component(props) { const [C, setComponent] = useState(); useEffect(() => { importComponent().then(setComponent); }, []); return C ? : null; }; } ================================================ FILE: examples/all-possible-containers/src/components/LazyComponent.js ================================================ import React from 'react'; import { EDIT_ME } from './_editMe'; import Counter from './Counter'; const LazyComponent = () => (
Lazy Component {EDIT_ME}
); export default LazyComponent; ================================================ FILE: examples/all-possible-containers/src/components/Modal.js ================================================ import React from 'react'; import ReactDOM from 'react-dom'; const modalRootId = 'modal-root'; let modalRoot = document.getElementById(modalRootId); if (!modalRoot) { modalRoot = document.createElement('div'); modalRoot.id = modalRootId; document.body.appendChild(modalRoot); } const styles = { modal: { position: 'absolute', top: '50%', left: '50%', right: 'auto', bottom: 'auto', border: '1px solid rgb(204, 204, 204)', background: 'rgb(255, 255, 255)', overflow: 'auto', borderRadius: '4px', outline: 'none', padding: '20px', zIndex: '1000', marginRight: '-50%', transform: 'translate(-50%, -50%)', }, closeButton: { position: 'absolute', top: '12px', right: '12px', background: 'url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMDAwMDAwIiBoZWlnaHQ9IjQ4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCiAgICA8cGF0aCBkPSJNMTkgNi40MUwxNy41OSA1IDEyIDEwLjU5IDYuNDEgNSA1IDYuNDEgMTAuNTkgMTIgNSAxNy41OSA2LjQxIDE5IDEyIDEzLjQxIDE3LjU5IDE5IDE5IDE3LjU5IDEzLjQxIDEyeiIvPg0KICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4NCjwvc3ZnPg==) no-repeat', backgroundSize: 'contain', width: '24px', height: '24px', cursor: 'pointer', }, content: { minWidth: '300px', maxWidth: '90vw', maxHeight: 'calc(100vh - 100px)', }, overlay: { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', backgroundColor: 'rgba(0, 0, 0, 0.5)', zIndex: '999', }, }; class Modal extends React.Component { constructor(props) { super(props); this.el = document.createElement('div'); Object.keys(styles.overlay).forEach(key => { this.el.style[key] = styles.overlay[key]; }); } props: { onRequestClose: Function, title: string, children: Object, hideTitle?: boolean, }; componentDidMount() { modalRoot.appendChild(this.el); } componentWillUnmount() { modalRoot.removeChild(this.el); } render() { const { onRequestClose, title, children, hideTitle } = this.props; return ReactDOM.createPortal(
{!hideTitle && ( )}
, this.el, ); } } export default Modal; ================================================ FILE: examples/all-possible-containers/src/components/ModalComponent.js ================================================ // @flow import React from 'react'; import Modal from './Modal'; import { EDIT_ME } from './_editMe'; const ModalComponent = ({ onRequestClose }: { onRequestClose: Function }) => ( {EDIT_ME} ); export default ModalComponent; ================================================ FILE: examples/all-possible-containers/src/components/PureClassComponent.js ================================================ import React from 'react'; import { EDIT_ME } from './_editMe'; class ClassComponent extends React.PureComponent { render() { return (
Pure Class Component {EDIT_ME}
); } } export default ClassComponent; ================================================ FILE: examples/all-possible-containers/src/components/SomethingWithHooks.js ================================================ ================================================ FILE: examples/all-possible-containers/src/components/_editMe.js ================================================ export const EDIT_ME = 'Edit me'; ================================================ FILE: examples/all-possible-containers/src/context.js ================================================ import React from 'react'; const Context = React.createContext('dummy-value'); export const connect = WrappedComponent => { const ConnectComponent = props => ( {value => } ); ConnectComponent.displayName = `Connect(${WrappedComponent.displayName || WrappedComponent.name || 'Unknown'})`; return ConnectComponent; }; export default Context; ================================================ FILE: examples/all-possible-containers/src/index.js ================================================ import React, { StrictMode } from 'react'; import { render } from 'react-dom'; import App from './components/App'; import Context from './context'; const appElement = document.createElement('div'); appElement.id = 'root'; document.body.appendChild(appElement); render( , appElement, ); ================================================ FILE: examples/all-possible-containers/src/wcl.js ================================================ 'use strict'; const fs = require('fs'); const path = require('path'); const { SourceNode, SourceMapConsumer } = require('source-map'); const { SourceMapGenerator } = require('source-map'); function transform(source, map) { if (source.indexOf('React.createElement') >= 0) { const separator = '\n\n;'; const appendText = ` /** @jsx _J$X_ */ var __react_jsx__ = require('react'); var _J$X_ = (__react_jsx__.default || __react_jsx__).createElement; `; return this.callback(null, [appendText, source.replace(/React\.createElement\(/g, '_J$X_(')].join(separator)); } if (source.match(/import (.*) from ['"]react["']/)) { const separator = '\n\n;'; const appendText = ` import * as TmpReact from 'react'; const _J$X_ = TmpReact.createElement; `; return this.callback(null, [appendText, source].join(separator)); } return this.callback(null, source, map); } module.exports = transform; ================================================ FILE: examples/all-possible-containers/webpack.config.js ================================================ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const exclude = absPath => /node_modules/.test(absPath); const mode = process.env.NODE_ENV || 'development'; const production = mode === 'production'; const wcl = require('./src/wcl'); module.exports = { mode, entry: './src/index.js', output: { filename: '[name].[hash].js', path: path.resolve(__dirname, 'dist'), publicPath: '/', chunkFilename: '[name].[chunkhash].js', devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), }, devtool: production ? false : 'eval-source-map', plugins: [ new webpack.DefinePlugin({ __DEV__: JSON.stringify(!production), }), new HtmlWebpackPlugin({ // Create index.html file cache: production, }), ], module: { strictExportPresence: true, rules: [ { test: /\.js$/, use: ['react-hot-loader/webpack'], }, { test: /\.js$/, exclude: /node_modules/, use: ['babel-loader'], }, ], }, optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { toplevel: true, mangle: true, }, }), ], splitChunks: { cacheGroups: { commons: { test: /[\\/]node_modules[\\/]/, // Create a vendor chunk with all the imported node_modules in it name: 'vendor', chunks: 'all', }, }, }, }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { // react: path.resolve(path.join(__dirname, './node_modules/react')), // 'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom')), // 'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')), // 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, bail: true, // Fail out on the first error instead of tolerating it }; ================================================ FILE: examples/async-components/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties", "dynamic-import-node"] } ================================================ FILE: examples/async-components/.gitignore ================================================ node_modules ================================================ FILE: examples/async-components/package.json ================================================ { "name": "hot-react-loadable", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "@hot-loader/react-dom": "^16.7.0-alpha.2.1", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "@loadable/component": "^4.0.2", "emotion": "^8.0.12", "loadable-components": "^2.2.2", "react": "^16.4.1", "react-async-component": "^2.0.0", "react-dom": "^16.4.1", "react-emotion": "^8.0.12", "react-hot-loader": "^4.5.2", "react-imported-component": "^5.2.0", "react-loadable": "^5.5.0", "react-remock": "^0.2.1", "react-universal-component": "^3.0.3", "styled-components": "^2.4.0" } } ================================================ FILE: examples/async-components/src/App.js ================================================ import { hot, setConfig } from 'react-hot-loader'; import * as React from 'react'; import styled from 'styled-components'; import emoStyled from 'react-emotion'; // import CAsync from './chunks/async-component' // import CLoadableComp from './chunks/loadable-components' import { RLoadable1, RLoadable2 } from './chunks/react-loadable'; // import CImp from './chunks/react-imported-component' // import CUni from './chunks/react-universal-component' const BigText = styled.div` font-size: 20px; `; const SmallText = emoStyled('div')` font-size: 22px; `; const indirect = { element: () => ( hidden ), }; const App = () => (
Testing React-Hot-Loader againts "React code splitting" components
    {/*
  • */} {/*Async-components */} {/*
  • */} {/*
  • */} {/*Loadable-components */} {/*
  • */}
  • React-Loadable 1
  • {/*
  • */} {/*Imported-component */} {/*
  • */} {/*
  • */} {/*Universal-component */} {/*
  • */}
); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/async-components/src/Counter.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { // this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return (
1#{this.state.count} 5
); } } export default Counter; ================================================ FILE: examples/async-components/src/Counter2.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { //return; this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 2 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return
2#{this.state.count}
; } } export default Counter; ================================================ FILE: examples/async-components/src/chunks/async-component.js ================================================ import { asyncComponent } from 'react-async-component'; export default asyncComponent({ resolve: () => import('../Counter'), }); ================================================ FILE: examples/async-components/src/chunks/loadable-components.js ================================================ // import loadable from 'loadable-components' // this is old API import loadable from '@loadable/component'; export const Loadable1 = loadable(() => import('../Counter')); export const Loadable2 = loadable(() => import('../Counter2')); ================================================ FILE: examples/async-components/src/chunks/react-imported-component.js ================================================ import imported from 'react-imported-component'; const test = 115582; export default imported(() => import('../Counter')); ================================================ FILE: examples/async-components/src/chunks/react-loadable.js ================================================ import React from 'react'; import Loadable from 'react-loadable'; export const RLoadable1 = Loadable({ loader: () => import('../Counter'), loading: () =>
, }); export const RLoadable2 = Loadable({ loader: () => import('../Counter2'), loading: () =>
, }); ================================================ FILE: examples/async-components/src/chunks/react-universal-component.js ================================================ import universal from 'react-universal-component'; export default universal(() => import('../Counter')); ================================================ FILE: examples/async-components/src/index.js ================================================ import 'babel-polyfill'; import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/async-components/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, // should work without exclude test: /\.js$/, use: ['react-hot-loader/webpack', 'babel-loader'], }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { react: path.resolve(path.join(__dirname, './node_modules/react')), 'react-dom': path.resolve(path.join(__dirname, './node_modules/@hot-loader/react-dom')), 'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')), 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/async-portals/.babelrc ================================================ { "presets": [ "env", "react" ], "plugins": [ "react-hot-loader/babel", "transform-class-properties", "dynamic-import-node" ] } ================================================ FILE: examples/async-portals/.gitignore ================================================ node_modules ================================================ FILE: examples/async-portals/package.json ================================================ { "name": "hot-async-portals", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.9.7" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next", "react-portal": "^4.1.2" } } ================================================ FILE: examples/async-portals/src/App.js ================================================ import { hot, setConfig } from 'react-hot-loader'; import * as React from 'react'; import Counter from './Counter'; import AsyncComponent from './async-component'; import Portal from './Portal'; const importer = () => import('./DeferredRender'); const Async = () => ; const App = () => (

{40}!

); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/async-portals/src/Counter.js ================================================ import React from 'react'; const RAND = Math.round(Math.random() * 1000); class Counter extends React.Component { state = { count: 0 }; gen = 0; componentDidMount() { this.setState({ count: RAND, }); } componentWillUnmount() { clearInterval(this.interval); } render() { // gen should change. count - no. return ( {this.state.count}:{this.gen++} ); } } export default Counter; ================================================ FILE: examples/async-portals/src/DeferredRender.js ================================================ import React from 'react'; import { Portal } from 'react-portal'; import hidden from './HiddenComponent'; const Hidden = hidden(); const APortal = () => ( This is a async portal ); export default () => (
ASYNC and
); ================================================ FILE: examples/async-portals/src/HiddenComponent.js ================================================ import React from 'react'; import Counter from './Counter'; const hidden = function() { return { counter: () => (
this is hidden counter()
), }; }; export default hidden; ================================================ FILE: examples/async-portals/src/Portal.js ================================================ import React from 'react'; import { Portal } from 'react-portal'; import hidden from './HiddenComponent'; const Hidden = hidden(); const InPortal = ({ children }) =>
+ {children}
; export default () => ( This is a first portal ); ================================================ FILE: examples/async-portals/src/async-component.js ================================================ import React, { Component } from 'react'; class AsyncComponent extends Component { constructor(props) { super(props); this.state = {}; } componentWillMount() { this.load(); } componentWillReceiveProps() { if (module.hot) { setImmediate(() => { this.load(); }); } } load() { return this.props.importer().then(payload => { this.setState({ AsyncComponent: payload.default }); }); } render() { const { AsyncComponent } = this.state; if (AsyncComponent) { return ; } return
async
; } } export default AsyncComponent; ================================================ FILE: examples/async-portals/src/index.js ================================================ import 'babel-polyfill'; import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/async-portals/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { //exclude: /node_modules|packages/, // should work without exclude test: /\.js$/, use: 'babel-loader', }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { react: path.resolve(path.join(__dirname, './node_modules/react')), 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/decorators/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties", "transform-decorators-legacy"] } ================================================ FILE: examples/decorators/.gitignore ================================================ node_modules ================================================ FILE: examples/decorators/package.json ================================================ { "name": "hot-decorators", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.9.7" }, "dependencies": { "autobind-decorator": "^2.1.0", "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next" } } ================================================ FILE: examples/decorators/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; const App = () => (

Hello, world.

); export default hot(module)(App); ================================================ FILE: examples/decorators/src/Counter.js ================================================ import React from 'react'; import autobind from 'autobind-decorator'; @autobind class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(this.increment, 200); } componentWillUnmount() { clearInterval(this.interval); } increment() { this.setState(prevState => ({ count: prevState.count + 1 })); } render() { return this.state.count; } } export default Counter; ================================================ FILE: examples/decorators/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/decorators/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/electron-typescript/.gitignore ================================================ .DS_Store node_modules dist ================================================ FILE: examples/electron-typescript/README.md ================================================ # Electron typescript ## Run application First you should start to watch application folder via webpack to enable hot-reloading ```bash yarn watch ``` In another terminal window run application ```bash yarn start ``` ## Compile application ```bash yarn build ``` In another terminal window run application ```bash yarn start ``` ================================================ FILE: examples/electron-typescript/app/App.tsx ================================================ import { hot } from 'react-hot-loader/root' import * as React from 'react' import Counter from './Counter' const App = () => (

Hello, world.

) export default hot(App) ================================================ FILE: examples/electron-typescript/app/Counter.tsx ================================================ import * as React from 'react' class Counter extends React.Component<{}, { count: number }> { interval: number constructor(props : any) { super(props) this.state = { count: 0 } } componentDidMount() { this.interval = window.setInterval( () => this.setState(prevState => ({ count: prevState.count + 1 })), 200, ) } generateString1() { return "1" } generateString2 = () => { return "1" } componentWillUnmount() { clearInterval(this.interval) } render() { return {this.state.count} - {this.generateString1()} - {this.generateString2()} } } export default Counter ================================================ FILE: examples/electron-typescript/app/development.html ================================================ Electron typescript example
================================================ FILE: examples/electron-typescript/app/index.tsx ================================================ import 'core-js/stable' import 'regenerator-runtime/runtime' import * as React from 'react' import { render } from 'react-dom' import App from './App' render(, document.getElementById('app')) ================================================ FILE: examples/electron-typescript/app/production.html ================================================ Electron typescript example
================================================ FILE: examples/electron-typescript/main.js ================================================ const { app, BrowserWindow } = require('electron'); const path = require('path'); let mainWindow; const createWindow = () => { mainWindow = new BrowserWindow({ width: 1000, height: 600, webPreferences: { nodeIntegration: true, }, }); mainWindow.loadFile(path.join(__dirname, 'dist/index.html')); mainWindow.on('closed', () => { mainWindow = null; }); mainWindow.on('ready-to-show', () => { mainWindow.show(); mainWindow.focus(); }); }; app.on('ready', () => { createWindow(); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); app.on('activate', () => { if (mainWindow === null) createWindow(); }); ================================================ FILE: examples/electron-typescript/package.json ================================================ { "name": "electron-typescript", "version": "1.0.0", "license": "MIT", "scripts": { "watch": "rm -rf ./dist && mkdir ./dist && cp ./app/development.html ./dist/index.html && NODE_ENV=development webpack-dev-server --hot", "build": "rm -rf ./dist && mkdir ./dist && cp ./app/production.html ./dist/index.html && NODE_ENV=development webpack", "start": "electron main.js" }, "main": "main.js", "devDependencies": { "@babel/core": "^7.6.0", "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-proposal-decorators": "^7.6.0", "@babel/preset-env": "^7.6.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.6.0", "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0", "babel-loader": "^8.0.6", "core-js": "^3.2.1", "electron": "^6.0.8", "fork-ts-checker-webpack-plugin": "^1.5.0", "react": "^16.9.0", "react-dom": "^16.9.0", "react-hot-loader": "^4.12.13", "regenerator-runtime": "^0.13.3", "typescript": "^3.6.3", "webpack": "^4.40.2", "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.1", "webpack-merge": "^4.2.2" } } ================================================ FILE: examples/electron-typescript/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es6", "jsx": "react" }, "include": ["./app/**/*"] } ================================================ FILE: examples/electron-typescript/webpack.config.js ================================================ const path = require('path'); const webpack = require('webpack'); const merge = require('webpack-merge'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const env = process.env.NODE_ENV; const appConfig = { mode: env || 'development', entry: ['./app/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { modules: ['node_modules'], alias: { 'react-dom': '@hot-loader/react-dom', }, extensions: ['.ts', '.tsx', '.js', '.jsx'], }, target: 'electron-renderer', module: { rules: [ { test: /\.(j|t)s(x)?$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { cacheDirectory: true, babelrc: false, presets: [ ['@babel/preset-env', { targets: { browsers: 'last 1 version' } }], '@babel/preset-typescript', '@babel/preset-react', ], plugins: [ // plugin-proposal-decorators is only needed if you're using experimental decorators in TypeScript ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }], 'react-hot-loader/babel', ], }, }, }, ], }, }; const developmentConfig = { output: { publicPath: 'http://localhost:8080/', }, plugins: [new ForkTsCheckerWebpackPlugin(), new webpack.NamedModulesPlugin()], devtool: 'eval-source-map', }; const productionConfig = { output: { publicPath: '/', }, }; module.exports = merge(appConfig, env === 'production' ? productionConfig : developmentConfig); ================================================ FILE: examples/hotCold/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/hotCold/.gitignore ================================================ node_modules ================================================ FILE: examples/hotCold/package.json ================================================ { "name": "hot-cold", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "emotion": "^8.0.12", "react": "^16.3.2", "react-circle": "^1.1.1", "react-dom": "^16.2.0", "react-emotion": "^8.0.12", "react-hot-loader": "^4.2.0", "styled-components": "^2.4.0" } } ================================================ FILE: examples/hotCold/src/App.js ================================================ import { hot, setConfig } from 'react-hot-loader'; import * as React from 'react'; import styled from 'styled-components'; import emoStyled from 'react-emotion'; import Circle from 'react-circle'; import Counter from './Counter'; const BigText = styled.div` font-size: 20px; `; const SmallText = emoStyled('div')` font-size: 22px; `; const indirect = { element: () => ( hidden ), }; const aNumber = 100500; const App = () => (

1.Hello, world! {aNumber}
2.Hello, world.

{BigText === .type ? 'BigText is cold' : 'BigText is hot'} (should be cold!)

{SmallText === .type ? 'SmallText is cold' : 'SmallText is hot'} (should be cold!)

{Circle === .type ? 'Circle is cold' : 'Circle is hot'} (should be cold!)

{Counter === .type ? 'Counter is cold' : 'Counter is hot'} (should be cold!)

{[depend on aNumber - , aNumber % 2 && ]}

); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/hotCold/src/Counter.js ================================================ import React from 'react'; import { cold } from 'react-hot-loader'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return
#{this.state.count}
; } } export default cold(Counter); //export default Counter; ================================================ FILE: examples/hotCold/src/index.js ================================================ import 'babel-polyfill'; import React from 'react'; import { render } from 'react-dom'; import './reactHotLoader.setup'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/hotCold/src/reactHotLoader.setup.js ================================================ import { setConfig, cold } from 'react-hot-loader'; setConfig({ onComponentRegister: (type, name, file) => file.indexOf('node_modules') > 0 && cold(type), onComponentCreate: (type, name) => name.indexOf('styled') > 0 && cold(type), }); ================================================ FILE: examples/hotCold/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const babelPlugin = path.resolve(__dirname, './node_modules/react-hot-loader/babel'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: ['react-hot-loader/webpack', 'babel-loader'], }, { include: /node_modules/, test: /\.js$/, use: { loader: 'react-hot-loader/webpack', }, }, /* // babel is an option, but slow option { include: /node_modules/, test: /\.js$/, use: { loader: 'babel-loader', options: { // plugins: ['react-hot-loader/babel'] // <<----- you need this plugins: [babelPlugin], // you DON'T need this }, }, }, /* */ ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { // you don't need this react: path.resolve(path.join(__dirname, './node_modules/react')), 'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')), 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/indeterminateComponent/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/indeterminateComponent/.gitignore ================================================ node_modules ================================================ FILE: examples/indeterminateComponent/package.json ================================================ { "name": "hot-indeternimate-component", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next" } } ================================================ FILE: examples/indeterminateComponent/src/App.js ================================================ import { hot, setConfig } from 'react-hot-loader'; import * as React from 'react'; import Counter from './Counter'; class AsyncMiddle1 extends React.Component { render() { return (
1:
); } } class AsyncMiddle2 extends React.Component { render() { return (
2:
); } } const DeferAsync = (props, context) => { if (props.n === 1) { return new AsyncMiddle1(props, context); } return new AsyncMiddle2(props, context); }; const SimpleWrapper = () => (
C: 44
); const App = () => (

1. 2. 3. 4.{' '} inner: test#5

); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/indeterminateComponent/src/Counter.js ================================================ import React from 'react'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return (
{this.state.count} {this.props.children && {this.props.children}}
); } } export default Counter; ================================================ FILE: examples/indeterminateComponent/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/indeterminateComponent/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { react: path.resolve(path.join(__dirname, './node_modules/react')), 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/mobx/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties", "transform-decorators-legacy"] } ================================================ FILE: examples/mobx/.gitignore ================================================ node_modules ================================================ FILE: examples/mobx/package.json ================================================ { "name": "hot-mobx", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "autobind-decorator": "^2.1.0", "mobx": "^5.0.3", "mobx-react": "^5.2.3", "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next" } } ================================================ FILE: examples/mobx/src/App.js ================================================ import React from 'react'; import { hot, setConfig } from 'react-hot-loader'; import Counter from './Counter'; const Element1 = ({ children }) =>
Block1 {children}
; const Element2 = () => (
Block2
); const App = () => (

Hello, mobx

); setConfig({ logLevel: 'debug' }); export default hot(module)(App); ================================================ FILE: examples/mobx/src/Counter.js ================================================ import React, { Component } from 'react'; import { hot } from 'react-hot-loader'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; class Counter extends React.Component { state = { count: Math.round(Math.random() * 1000) }; componentDidMount() { //this.interval = setInterval(this.increment, 200) } componentWillUnmount() { clearInterval(this.interval); } increment = () => { this.setState(prevState => ({ count: prevState.count + 1 })); }; render() { return this.state.count; } } class Comp extends Component { state = { obsObj: null, }; static getDerivedStateFromProps(nextProps, prevState) { return { obsObj: nextProps.obsObj }; } componentDidMount() { //setInterval(() => this.setState({update: 1}), 5000); } render() { const { prop1 } = this.state.obsObj || {}; return (
{prop1}
); } } const ObsComp = observer(Comp); class App extends Component { @observable obj = { prop1: 'Example', }; render() { return (
); } } export default App; ================================================ FILE: examples/mobx/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/mobx/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { react: path.resolve(path.join(__dirname, './node_modules/react')), 'babel-core': path.resolve(path.join(__dirname, './node_modules/@babel/core')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/multiple-hocs/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/multiple-hocs/.gitignore ================================================ node_modules ================================================ FILE: examples/multiple-hocs/package.json ================================================ { "name": "hot-multiple-hocs", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next", "recompose": "^0.26.0" } } ================================================ FILE: examples/multiple-hocs/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; const App = () => (

Hello, world.

); export default hot(module)(App); ================================================ FILE: examples/multiple-hocs/src/Counter.js ================================================ import React from 'react'; import * as r from 'recompose'; const Counter = r.compose( r.pure, r.withProps({ style: { color: 'red' } }), r.withState('count', 'setCount', 0), r.lifecycle({ componentDidMount() { this.interval = setInterval(() => this.props.setCount(this.props.count + 1), 200); }, componentWillUnmount() { clearInterval(this.interval); }, }), )(({ style, count }) =>
{count}
); export default Counter; ================================================ FILE: examples/multiple-hocs/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/multiple-hocs/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/parcel/.babelrc ================================================ { "presets": ["env"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/parcel/.gitignore ================================================ node_modules .cache dist ================================================ FILE: examples/parcel/index.html ================================================ My React Project
================================================ FILE: examples/parcel/package.json ================================================ { "name": "hot-parcel", "version": "1.0.0", "license": "MIT", "scripts": { "start": "parcel index.html --no-cache", "start:prod": "parcel build index.html", "start:link": "cp -R ../../dist ./node_modules/react-hot-loader && cp -R ../../*.js ./node_modules/react-hot-loader" }, "devDependencies": { "@babel/core": "^7.4.5", "@babel/preset-env": "^7.4.5", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.7.0", "parcel-bundler": "^1.12.3" }, "dependencies": { "@hot-loader/react-dom": "^16.8.6", "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.11.1" }, "alias": { "react-dom": "@hot-loader/react-dom" } } ================================================ FILE: examples/parcel/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; import { Problem } from './Problem'; const App = () => { return (

Hello, world.

); }; export default hot(module)(App); ================================================ FILE: examples/parcel/src/Counter.js ================================================ import React from 'react'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return this.state.count; } } export default Counter; export function useExternalHook() { React.useEffect(() => {}); } ================================================ FILE: examples/parcel/src/Problem.js ================================================ import React from 'react'; import { useExternalHook } from './Counter'; export function Problem() { useExternalHook(); return
the problem!!!!
; } ================================================ FILE: examples/parcel/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; function renderApp() { render(, root); } renderApp(); module.hot.accept(); ================================================ FILE: examples/preact/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/preact/.gitignore ================================================ node_modules ================================================ FILE: examples/preact/package.json ================================================ { "name": "hot-preact-simple", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "preact": "^8.2.7", "preact-compat": "^3.18.0", "react-hot-loader": "^4.1.2" }, "resolutions": { "react": "preact-compat", "react-dom": "preact-compat" } } ================================================ FILE: examples/preact/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; import { Internal } from './Internal'; const App = () => (

Hello, world

); export default hot(module)(App); ================================================ FILE: examples/preact/src/Counter.js ================================================ import { h, render, Component } from 'preact'; // Tell Babel to transform JSX into h() calls: /** @jsx h */ class Counter extends Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render(props, state) { return
10:{state.count}
; } } export default Counter; ================================================ FILE: examples/preact/src/Internal.js ================================================ import preact from 'preact'; import Counter from './Counter'; // Tell Babel to transform JSX into h() calls: /** @jsx preact.h */ export const Internal = () => (
); ================================================ FILE: examples/preact/src/hotLoaderSetup.js ================================================ import reactHotLoader, { setConfig } from 'react-hot-loader'; import preact from 'preact'; reactHotLoader.preact(preact); ================================================ FILE: examples/preact/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; // setup Preact before importing App import './hotLoaderSetup'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/preact/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, resolve: { alias: { react: path.resolve(path.join(__dirname, './node_modules/preact-compat')), 'react-dom': path.resolve(path.join(__dirname, './node_modules/preact-compat')), }, }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/react-bootstrap/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/react-bootstrap/.gitignore ================================================ node_modules ================================================ FILE: examples/react-bootstrap/package.json ================================================ { "name": "hot-react-bootstrap", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "react": "^16.2.0", "react-bootstrap": "^0.31.5", "react-dom": "^16.2.0", "react-hot-loader": "next" } } ================================================ FILE: examples/react-bootstrap/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader/root'; import FormControl from 'react-bootstrap/lib/FormControl'; class App extends React.Component { render() { return (

Hello, world.

); } } export default hot(App); ================================================ FILE: examples/react-bootstrap/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/react-bootstrap/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/react-refetch/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/react-refetch/.gitignore ================================================ node_modules ================================================ FILE: examples/react-refetch/package.json ================================================ { "name": "hot-react-refetch", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next", "react-refetch": "^1.0.4" } } ================================================ FILE: examples/react-refetch/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader/root'; import Characters from './Characters'; const App = () => (

Hello, world.

); export default hot(App); ================================================ FILE: examples/react-refetch/src/Characters.js ================================================ import React from 'react'; import { connect } from 'react-refetch'; const Characters = ({ charactersFetch }) => charactersFetch.fulfilled ? (
    {charactersFetch.value.results.map(result =>
  • {result.name}
  • )}
) : null; export default connect(() => ({ charactersFetch: 'https://swapi.co/api/people/', }))(Characters); ================================================ FILE: examples/react-refetch/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/react-refetch/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/redux/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/redux/.gitignore ================================================ node_modules ================================================ FILE: examples/redux/index_csp.html ================================================ Webpack App ================================================ FILE: examples/redux/package.json ================================================ { "name": "hot-redux", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^3.2.0", "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.10" }, "dependencies": { "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.12.5", "react-redux": "^7.1.0", "redux": "^4.0.3" } } ================================================ FILE: examples/redux/src/App.js ================================================ import React, { useState, useEffect } from 'react'; import { hot } from 'react-hot-loader/root'; import { MyComponent } from './MyComponent'; import { HookComponent } from './HookComponent'; const App = () => { const [state] = useState(42); const [effect, setEffect] = useState(42); useEffect(() => { setEffect(effect + 1); }, []); useEffect( () => { setEffect(effect + 0.1); }, ['hot'], ); return (
test {state} : {effect}
); }; export default hot(App); ================================================ FILE: examples/redux/src/HookComponent.js ================================================ import { useSelector } from 'react-redux'; export function HookComponent() { const notifications = useSelector(() => true); return null; } ================================================ FILE: examples/redux/src/MyComponent.js ================================================ import React from 'react'; import { connect } from 'react-redux'; export class MyComponent1 extends React.Component { render() { return
{this.props.children}
; } } export const MyComponent = connect(state => state, undefined, undefined, { pure: false })(MyComponent1); ================================================ FILE: examples/redux/src/index.js ================================================ import React from 'react'; import {Provider} from 'react-redux'; import {createStore, combineReducers} from 'redux'; import {render} from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); const store =createStore(combineReducers({})); render( , root); ================================================ FILE: examples/redux/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], mode: process.env.NODE_ENV || 'development', //devtool: false, output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /packages/, // should work without exclude test: /\.js$/, use: ['react-hot-loader/webpack', 'babel-loader'], }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { // 'react-dom': '@hot-loader/react-dom', // react: path.resolve(path.join(__dirname, './node_modules/react')), // 'react-hot-loader': path.resolve( // path.join(__dirname, './node_modules/react-hot-loader'), // ), // 'babel-core': path.resolve( // path.join(__dirname, './node_modules/@babel/core'), // ), }, }, plugins: [ new HtmlWebpackPlugin({ // uncomment this line to test RHL in "secure" env // template: "index_csp.html", }), new webpack.NamedModulesPlugin(), ], }; ================================================ FILE: examples/styled-components/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties", "dynamic-import-node"] } ================================================ FILE: examples/styled-components/.gitignore ================================================ node_modules ================================================ FILE: examples/styled-components/index_csp.html ================================================ Webpack App ================================================ FILE: examples/styled-components/package.json ================================================ { "name": "hot-styled-components", "version": "1.0.0", "license": "MIT", "scripts": { "start": "NODE_ENV=development webpack-dev-server --hot", "start:cold": "NODE_ENV=development webpack-dev-server", "start:prod": "NODE_ENV=production webpack-dev-server --hot", "start:link": "cp -R ../../dist ./node_modules/react-hot-loader && cp -R ../../*.js ./node_modules/react-hot-loader", "start:hot": "cp -R ../../../hot/react-dom/target/cjs ./node_modules/react-dom" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^3.2.0", "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.10" }, "dependencies": { "babel-polyfill": "^6.26.0", "emotion": "^8.0.12", "react": "^17.0.0-rc.2", "react-dom": "^17.0.0-rc.2", "react-emotion": "^9.2.12", "react-hot-loader": "^4.12.21", "react-spring": "^8.0.25", "styled-components": "^4.0.3" } } ================================================ FILE: examples/styled-components/src/App.js ================================================ import { hot } from 'react-hot-loader/root'; import { setConfig } from 'react-hot-loader'; import * as React from 'react'; import styled from 'styled-components'; import emoStyled from 'react-emotion'; import './config'; import Counter from './Counter'; import { SpringTest } from './Spring'; // const genApp = () => { const BigText = styled.div` font-size: 25px; `; const SmallText = emoStyled('div')` font-size: 22px; `; const indirect = { element: () => ( hidden ), }; const indirectStyled = { DS: styled.div` border: 20px solid #f00; `, DE: emoStyled('div')`border: 1px solid #F00`, }; const Async = React.lazy(() => import('./Async')); const aNumber = 200500; const OtherComponent = () => test; const Context = React.createContext(); const Hook = () => { const [state, setState] = React.useState({ x: 4 }); React.useState(0); React.useEffect(() => { console.log('mount effected 1'); setState(state => ({ x: state.x + 1, })); }, []); React.useEffect( () => { console.log('hot effected'); setState(state => ({ x: state.x + 0.5, })); }, ['hot'], ); //React.useState(0); return (
hook state 1: {state.x}
); }; const Memo1 = React.memo(() => (
[mem memo]
)); const Memo2 = React.memo( class extends React.Component { render() { return (
[mem2 memo]
); } }, ); const Memo3 = React.memo( React.forwardRef(() => (
[double memo 3 memo]
)), ); const TwinComponents = [ ({ children }) =>
{children}
, ({ children }) =>
{children}
, ]; const TwinComponent = props => { const Twin = TwinComponents[window.twinId || 0]; return ; }; const InApp = () => (

1. Hello, world! {aNumber} hook:
2.Hello, world! .
{' '} indirect DS {' '} {' '} indirect DE {' '}
{[depend on aNumber - , aNumber % 2 && ]}

); const App = () => ; setConfig({ logLevel: 'debug', hotHooks: true, }); // return App; // } // // const App = genApp(); export default hot(App); ================================================ FILE: examples/styled-components/src/Async.js ================================================ import React from 'react'; import Counter from './Counter'; export default () => (
async
); ================================================ FILE: examples/styled-components/src/Counter.js ================================================ import React, { useContext, useState, useEffect, useRef } from 'react'; const TimerContext = React.createContext(0); const ComponentA = () => { const value = useContext(TimerContext); const [state] = useState('A'); return (
{state}-{value}- {v => v}
); }; const ComponentB = () => { const [state] = useState('B'); const value = useContext(TimerContext); return (
{state}-{value}- {v => v}
); }; const useSomeHandler = function() { return 43; }; function RippleComponent() { function useRippleHandler() {} useRippleHandler(); useRippleHandler(); return false; } const Counter = ({ children }) => { const [count, setState] = useState(0); const useRippleHandler = function() {}; useRippleHandler(); useSomeHandler(); useState(0); const ref = useRef(); useEffect(() => { ref.current = 0; const int = setInterval(() => setState(ref.current++), 1000); return () => clearInterval(int); }, []); return (
#{count} {children && React.cloneElement(children, { counter: count, })} {count % 2 ? : }
); }; export default Counter; ================================================ FILE: examples/styled-components/src/Spring.js ================================================ // import { animated, useSpring } from 'react-spring'; import React, { useCallback, useState } from 'react'; import Counter from './Counter'; const context = React.createContext('test1'); const Test = () => { const v = React.useContext(context); return (
--{v}-- ##
); }; export function SpringTest() { const [thingDone, toggleThingDone] = useState(false); const doTheThing = useCallback(() => toggleThingDone(!thingDone), [thingDone]); // const fader = useSpring({ opacity: thingDone ? 1 : 0 }); const v = React.useContext(context); return ( {v} {/*You did the thing!*/} ); } ================================================ FILE: examples/styled-components/src/config.js ================================================ import { setConfig } from 'react-hot-loader'; import ReactDOM from 'react-dom'; setConfig({ ignoreSFC: !!ReactDOM.setHotElementComparator, pureSFC: true, pureRender: true, }); ================================================ FILE: examples/styled-components/src/index.js ================================================ import 'react-lifecycles-compat'; import 'core-js/modules/es6.promise'; // import 'babel-polyfill'; import React from 'react'; import { render } from 'react-dom'; import App from './App'; //const root = ; document.body.appendChild(document.createElement('div')); render(, document.getElementsByTagName('div')[0]); ================================================ FILE: examples/styled-components/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], mode: process.env.NODE_ENV || 'development', //devtool: false, output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules/, // should work without exclude test: /\.js$/, use: ['babel-loader'], }, { include: /node_modules\/react-lifecycles-compat/, // should work without exclude test: /\.js$/, use: ['babel-loader'], }, { include: /node_modules\/react-dom/, test: /\.jsx?$/, use: { loader: 'react-hot-loader/webpack', options: { noRegister: true, cacheBreaker: 1, }, }, }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { // 'react-dom': '@hot-loader/react-dom', // react: path.resolve(path.join(__dirname, './node_modules/react')), // 'react-hot-loader': path.resolve( // path.join(__dirname, './node_modules/react-hot-loader'), // ), // 'babel-core': path.resolve( // path.join(__dirname, './node_modules/@babel/core'), // ), }, }, plugins: [ new HtmlWebpackPlugin({ // uncomment this line to test RHL in "secure" env // template: "index_csp.html", }), new webpack.NamedModulesPlugin(), ], }; ================================================ FILE: examples/typescript/.gitignore ================================================ node_modules ================================================ FILE: examples/typescript/package.json ================================================ { "name": "hot-typescript", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "@babel/core": "^7.6.0", "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-proposal-decorators": "^7.6.0", "@babel/preset-env": "^7.6.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.6.0", "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0", "babel-loader": "^8.0.6", "fork-ts-checker-webpack-plugin": "^1.5.0", "html-webpack-plugin": "^3.2.0", "typescript": "^3.6.3", "webpack": "^4.40.2", "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.1" }, "dependencies": { "@hot-loader/react-dom": "^16.10.2", "core-js": "^3.2.1", "react": "^16.9.0", "react-dom": "^16.9.0", "react-hot-loader": "^4.12.13", "regenerator-runtime": "^0.13.3" } } ================================================ FILE: examples/typescript/src/App.tsx ================================================ import { hot } from 'react-hot-loader/root' import * as React from 'react' import Counter from './Counter' const App = () => (

Hello, world.

) export default hot(App) ================================================ FILE: examples/typescript/src/Counter.tsx ================================================ import * as React from 'react' class Counter extends React.Component<{}, { count: number }> { interval: number constructor(props : any) { super(props) this.state = { count: 0 } } componentDidMount() { this.interval = window.setInterval( () => this.setState(prevState => ({ count: prevState.count + 1 })), 200, ) } generateString1() { return "1" } generateString2 = () => { return "1" } componentWillUnmount() { clearInterval(this.interval) } render() { return {this.state.count} - {this.generateString1()} - {this.generateString2()} } } export default Counter ================================================ FILE: examples/typescript/src/index.tsx ================================================ import 'core-js/stable' import 'regenerator-runtime/runtime' import * as React from 'react' import { render } from 'react-dom' import App from './App' const root = document.createElement('div') document.body.appendChild(root) render(, root) ================================================ FILE: examples/typescript/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es6", "jsx": "react" }, "include": ["./src/**/*"] } ================================================ FILE: examples/typescript/webpack.config.babel.js ================================================ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); module.exports = { mode: 'development', entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { modules: ['node_modules'], alias: { 'react-dom': '@hot-loader/react-dom', }, extensions: ['.ts', '.tsx', '.js', '.jsx'], }, module: { rules: [ { test: /\.(j|t)s(x)?$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { cacheDirectory: true, babelrc: false, presets: [ [ '@babel/preset-env', { targets: { browsers: 'last 2 versions' } }, // or whatever your project requires ], '@babel/preset-typescript', '@babel/preset-react', ], plugins: [ // plugin-proposal-decorators is only needed if you're using experimental decorators in TypeScript ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }], 'react-hot-loader/babel', ], }, }, }, ], }, devtool: 'eval-source-map', plugins: [new ForkTsCheckerWebpackPlugin(), new webpack.NamedModulesPlugin(), new HtmlWebpackPlugin()], }; ================================================ FILE: examples/typescript-no-babel/.gitignore ================================================ node_modules ================================================ FILE: examples/typescript-no-babel/README.md ================================================ # Attention!!! - Hook reloading is relaying on babel plugin! This example don't support hooks reload. If you want to have hook reloading, use typescript with babel. ================================================ FILE: examples/typescript-no-babel/package.json ================================================ { "name": "hot-typescript-pure", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "awesome-typescript-loader": "^5.2.1", "html-webpack-plugin": "^3.2.0", "typescript": "^3.6.3", "webpack": "^4.40.2", "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.1" }, "dependencies": { "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0", "react": "^16.9.0", "react-dom": "^16.9.0", "react-hot-loader": "^4.12.13" } } ================================================ FILE: examples/typescript-no-babel/src/App.tsx ================================================ import { hot } from 'react-hot-loader/root' import * as React from 'react' import Counter from './Counter' const App = () => (

Hello, world!
You can update this text, and it will work

) export default hot(App) ================================================ FILE: examples/typescript-no-babel/src/Counter.tsx ================================================ import * as React from 'react' class Counter extends React.Component<{}, { count: number }> { interval: number constructor(props: any) { super(props) this.state = { count: 0 } } componentDidMount() { this.interval = window.setInterval( () => this.setState(prevState => ({ count: prevState.count + 1 })), 200, ) } generateString1() { // you can update this method, and it will work return "1" } generateString2 = () => { // this one will not return "1" } componentWillUnmount() { clearInterval(this.interval) } render() { return {this.state.count} - {this.generateString1()} - {this.generateString2()} } } export default Counter ================================================ FILE: examples/typescript-no-babel/src/index.tsx ================================================ import * as React from 'react' import { render } from 'react-dom' import App from './App' const root = document.createElement('div') document.body.appendChild(root) render(, root) ================================================ FILE: examples/typescript-no-babel/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es6", "jsx": "react" }, "include": ["./src/**/*"] } ================================================ FILE: examples/typescript-no-babel/webpack.config.js ================================================ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { modules: ['node_modules'], alias: { 'react-dom': '@hot-loader/react-dom', }, extensions: ['.ts', '.tsx', '.js', '.jsx'], }, module: { rules: [ { test: /\.ts(x)?$/, use: ['awesome-typescript-loader'], }, ], }, devtool: 'eval-source-map', plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/webpack/.babelrc ================================================ { "presets": ["env", "react"], "plugins": ["react-hot-loader/babel", "transform-class-properties"] } ================================================ FILE: examples/webpack/.gitignore ================================================ node_modules ================================================ FILE: examples/webpack/package.json ================================================ { "name": "hot-webpack", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^2.30.1", "webpack": "^3.10.0", "webpack-dev-server": "^3.1.11" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "next" } } ================================================ FILE: examples/webpack/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; const App = () => (

Hello, world.

); export default hot(module)(App); ================================================ FILE: examples/webpack/src/Counter.js ================================================ import React from 'react'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return this.state.count; } } export default Counter; ================================================ FILE: examples/webpack/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/webpack/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: examples/webpack-modern/.babelrc ================================================ { "presets": [ [ "@babel/preset-env", { "loose": true, "modules": false, "targets": { "browsers": "last 2 chrome versions" } } ], "@babel/preset-react" ], "plugins": [ "react-hot-loader/babel", ["@babel/plugin-proposal-class-properties", { "loose": true }] ] } ================================================ FILE: examples/webpack-modern/.gitignore ================================================ node_modules ================================================ FILE: examples/webpack-modern/package.json ================================================ { "name": "hot-webpack-modern", "version": "1.0.0", "license": "MIT", "scripts": { "start": "webpack-dev-server --hot" }, "devDependencies": { "@babel/core": "^7.4.4", "@babel/plugin-proposal-class-properties": "^7.4.4", "@babel/preset-env": "^7.4.4", "@babel/preset-react": "^7.0.0", "@hot-loader/react-dom": "^16.8.6", "babel-loader": "^8.0.5", "html-webpack-plugin": "^3.2.0", "webpack": "^4.31.0", "webpack-cli": "^3.3.2", "webpack-dev-server": "^3.3.1" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-hot-loader": "^4.3.3" } } ================================================ FILE: examples/webpack-modern/src/App.js ================================================ import React from 'react'; import { hot } from 'react-hot-loader'; import Counter from './Counter'; const App = () => (

Hello, world.

); export default hot(module)(App); ================================================ FILE: examples/webpack-modern/src/Counter.js ================================================ import React from 'react'; class Counter extends React.Component { state = { count: 0 }; componentDidMount() { this.interval = setInterval(() => this.setState(prevState => ({ count: prevState.count + 1 })), 200); } componentWillUnmount() { clearInterval(this.interval); } render() { return this.state.count; } } export default Counter; ================================================ FILE: examples/webpack-modern/src/index.js ================================================ import React from 'react'; import { render } from 'react-dom'; import App from './App'; const root = document.createElement('div'); document.body.appendChild(root); render(, root); ================================================ FILE: examples/webpack-modern/webpack.config.babel.js ================================================ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', entry: ['./src/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { alias: { 'react-dom': '@hot-loader/react-dom', }, }, module: { rules: [ { exclude: /node_modules|packages/, test: /\.js$/, use: 'babel-loader', }, ], }, plugins: [new HtmlWebpackPlugin(), new webpack.NamedModulesPlugin()], }; ================================================ FILE: index.d.ts ================================================ import * as React from 'react'; interface ErrorReporterProps { error: any; } export interface AppContainerProps { errorBoundary?: boolean; errorReporter?: React.ComponentType; } export interface AppChildren { children?: React.ReactElement; } export class AppContainer extends React.Component {} /** * Marks module and a returns a HOC to mark a Component inside it as hot-exported * @param {NodeModuleObject} module ALWAYS should be just "module". * @return {function} "hot" HOC. * * @example marks current module as hot, and export MyComponent as HotExportedMyComponent * export default hot(module)(MyComponent) */ export function hot(module: any): >(Component: T, props?: AppContainerProps) => T; /** * Marks component as `cold`, and making it invisible for React-Hot-Loader. * Any changes to that component will cause local state loss. * @param {React.ComponentType} component to chill * @return {React.ComponentType} component, as it was passed in. * * @example marks some component as cold * export default cold(MyComponent) */ export function cold>(component: T): T; /** * Tests are types of two components equal * @param {Component} typeA * @param {Component} typeB * @return {boolean} are they equal * * @example test that some rendered component(ReactElement), has the same type as BaseComponent * areComponentEqual(RenderedComponent.type, BaseComponent) */ export function areComponentsEqual(typeA: React.ComponentType, typeB: React.ComponentType): boolean; export interface HotError { error: Error; errorInfo?: React.ErrorInfo; } export interface Config { /** * Specify loglLevel, default to 'error', set it to false to disable logs. * Available levels: ['debug', 'log', 'warn', 'error'] */ logLevel: string; /** * * @param {any} type being registered. This could be ANY top level variable among project. * @param {string} uniqueLocalName - variable name * @param {string} fileName - origin file * @return {any} */ onComponentRegister: (type: any, uniqueLocalName: string, fileName: string) => any; /** * * @param type {any} type being rendered. The first argument of React.createElement * @param displayName {string} type display name (if exists) */ onComponentCreate: (type: any, displayName: string) => any; /** * Allows using SFC without changes. leading to some components not updated */ pureSFC: boolean; /** * keep render method unpatched, moving sideEffect to componentDidUpdate */ pureRender: boolean; /** * Allows SFC to be used, enables "intermediate" components used by Relay, should be disabled for Preact */ allowSFC: boolean; /** * Disable "hot-replacement-render" */ disableHotRenderer: boolean; /** * Disable "hot-replacement-render" when injection into react-dom are made */ disableHotRendererWhenInjected: boolean; /** * Show "hot-loader/react-dom" warning */ showReactDomPatchNotification: boolean; /** * flag to completely disable RHL for SFC */ ignoreSFC: boolean; /** * flag to completely disable RHL for Components */ ignoreComponents: boolean; /** * enables or disables hooks treatment */ reloadHooks: boolean; /** * default value for AppContainer errorOverlay */ errorReporter: React.ComponentType; /** * Global error overlay */ ErrorOverlay: React.ComponentType<{ errors: Array }>; /** * Controls tail(deferred) update checking */ trackTailUpdates: boolean; } /** * Confugures how React Hot Loader works * @param {Config} config */ export function setConfig(config: Partial): void; ================================================ FILE: index.js ================================================ 'use strict'; if (process.env.NODE_ENV === 'production') { module.exports = require('./dist/react-hot-loader.production.min.js'); } else if (process.env.NODE_ENV === 'test') { module.exports = require('./dist/react-hot-loader.production.min.js'); } else if (typeof window === 'undefined') { // this is just server environment module.exports = require('./dist/react-hot-loader.production.min.js'); } else if (!module.hot) { module.exports = require('./dist/react-hot-loader.production.min.js'); module.exports.AppContainer.warnAboutHMRDisabled = true; module.exports.hot.shouldWrapWithAppContainer = true; } else { var evalAllowed = false; var evalError = null; try { eval('evalAllowed = true'); } catch (e) { // eval not allowed due to CSP evalError = e && e.message ? e.message : 'unknown reason'; } // TODO: dont use eval to update methods. see #1273 // RHL needs setPrototypeOf to operate Component inheritance, and eval to patch methods var jsFeaturesPresent = !!Object.setPrototypeOf; if (!jsFeaturesPresent || !evalAllowed) { // we are not in prod mode, but RHL could not be activated console.warn( 'React-Hot-Loader is not supported in this environment:', [ !jsFeaturesPresent && "some JS features are missing", !evalAllowed && "`eval` is not allowed(" + evalError + ")" ].join(','), '.' ); module.exports = require('./dist/react-hot-loader.production.min.js'); } else { module.exports = window.reactHotLoaderGlobal = require('./dist/react-hot-loader.development.js'); } } ================================================ FILE: package.json ================================================ { "name": "react-hot-loader", "version": "4.13.1", "description": "Tweak React components in real time.", "main": "index.js", "types": "index.d.ts", "homepage": "https://github.com/gaearon/react-hot-loader", "repository": "https://github.com/gaearon/react-hot-loader/", "license": "MIT", "author": "Dan Abramov", "scripts": { "build": "rollup -c", "ci": "scripts/ci.sh", "format": "prettier --write \"**/*.{js,md,ts,json}\" *.{js,md,ts,json}", "lint": "eslint .", "prepublishOnly": "yarn build", "prebuild": "rm -rf dist", "precommit": "lint-staged", "release": "standard-version && conventional-github-releaser -p angular", "test": "yarn test:es2015 && yarn test:modern", "test:es2015": "cross-env BABEL_TARGET=es2015 jest --no-cache", "test:modern": "cross-env BABEL_TARGET=modern jest --no-cache", "test:react-dom:prepare": "rm -Rf ./test/hot/react-dom && node ./test/hot/createPatchedReact.js", "yarn-deduplicate": "yarn-deduplicate yarn.lock -s fewer" }, "lint-staged": { "*.{js,md,ts,json}": [ "prettier --write", "git add" ] }, "files": [ "dist", "index.js", "babel.js", "webpack.js", "patch.js", "root.js", "*.d.ts" ], "sideEffects": false, "keywords": [ "react", "javascript", "webpack", "hmr", "livereload", "live", "edit", "hot", "loader", "reload" ], "devDependencies": { "@hot-loader/react-dom": "^16.8.6", "@types/react": "16", "@wojtekmaj/enzyme-adapter-react-17": "^0.1.1", "babel-cli": "^6.7.5", "babel-core": "^6.26.3", "babel-eslint": "^8.2.3", "babel-jest": "^22.4.3", "babel-plugin-dynamic-import-node": "^1.2.0", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.5.0", "codecov": "^3.0.1", "conventional-github-releaser": "^3.1.3", "create-react-class": "^15.6.3", "cross-env": "^5.1.4", "enzyme": "^3.7.0", "enzyme-adapter-react-15": "^1.0.5", "enzyme-adapter-react-16": "^1.6.0", "eslint": "^4.19.1", "eslint-config-airbnb": "^16.0.0", "eslint-config-prettier": "^2.6.0", "eslint-plugin-import": "^2.11.0", "eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-react": "^7.7.0", "husky": "^0.14.3", "jest": "^22.4.3", "lint-staged": "9.4.1", "prettier": "^1.12.1", "react": "16", "react-dom": "16", "react-mount": "^0.1.3", "react-test-renderer": "16", "recompose": "^0.27.0", "rimraf": "^2.5.2", "rollup": "^0.58.2", "rollup-plugin-babel": "^3.0.4", "rollup-plugin-commonjs": "^9.1.3", "rollup-plugin-json": "^2.3.0", "rollup-plugin-node-resolve": "^3.3.0", "rollup-plugin-replace": "^2.0.0", "rollup-plugin-uglify": "^3.0.0", "standard-version": "^7.0.0", "yarn-deduplicate": "^1.1.1" }, "peerDependencies": { "@types/react": "^15.0.0 || ^16.0.0 || ^17.0.0", "react": "^15.0.0 || ^16.0.0 || ^17.0.0", "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true } }, "dependencies": { "fast-levenshtein": "^2.0.6", "global": "^4.3.0", "hoist-non-react-statics": "^3.3.0", "loader-utils": "^2.0.3", "prop-types": "^15.6.1", "react-lifecycles-compat": "^3.0.4", "shallowequal": "^1.1.0", "source-map": "^0.7.3" }, "engines": { "node": ">= 6" }, "jest": { "moduleDirectories": [ "node_modules", "" ], "setupFiles": [ "/testConfig/setupTests.js" ], "transform": { "^.+\\.js$": "/testConfig/babel.js" } }, "collective": { "type": "opencollective", "url": "https://opencollective.com/react-hot-loader" } } ================================================ FILE: patch.js ================================================ 'use strict'; if (!module.hot || process.env.NODE_ENV === 'production') { module.exports = require('./dist/react-hot-loader.production.min.js'); } else { module.exports = require('./dist/react-hot-loader.development.js'); } ================================================ FILE: rollup.config.js ================================================ /* eslint-disable flowtype/require-valid-file-annotation, no-console, import/extensions */ import nodeResolve from 'rollup-plugin-node-resolve'; import replace from 'rollup-plugin-replace'; import commonjs from 'rollup-plugin-commonjs'; import babel from 'rollup-plugin-babel'; import json from 'rollup-plugin-json'; import uglify from 'rollup-plugin-uglify'; import pkg from './package.json'; const commonPlugins = [ json(), nodeResolve(), babel({ plugins: ['external-helpers'] }), commonjs({ ignoreGlobal: true }), ]; const getConfig = (input, dist, env) => ({ input, external: ['react-dom', 'react', 'fs', 'path'].concat(Object.keys(pkg.dependencies)), plugins: commonPlugins .concat( env ? [ replace({ 'process.env.NODE_ENV': JSON.stringify(env), }), ] : [], ) .concat(env === 'production' ? [uglify()] : []), output: [ { file: dist, format: 'cjs', exports: 'named', globals: { react: 'React' }, }, ], }); export default [ getConfig('src/index.dev.js', 'dist/react-hot-loader.development.js', 'development'), getConfig('src/index.prod.js', 'dist/react-hot-loader.production.min.js', 'production'), getConfig('src/babel.dev.js', 'dist/babel.development.js', 'development'), getConfig('src/babel.prod.js', 'dist/babel.production.min.js', 'production'), getConfig('src/webpack/index.js', 'dist/webpack.development.js', 'development'), getConfig('src/webpack/index.js', 'dist/webpack.production.min.js', 'production'), { input: 'src/webpack/webpackTagCommonJSExports.js', plugins: [babel()], output: { file: 'dist/webpackTagCommonJSExports.js', format: 'cjs', }, }, ]; ================================================ FILE: root.d.ts ================================================ import * as React from 'react'; import { AppContainerProps } from './index'; export function hot>(Component: T, props?: AppContainerProps): T; ================================================ FILE: root.js ================================================ if (process.env.NODE_ENV !== 'production') { var hot = require('./index').hot; if (module.hot) { var cache = require.cache; if (!module.parents || module.parents.length === 0) { throw new Error( 'React-Hot-Loader: `react-hot-loader/root` is not supported on your system. ' + 'Please use `import {hot} from "react-hot-loader"` instead' ); } // access parent var parent = cache[module.parents[0]]; if (!parent) { throw new Error( 'React-Hot-Loader: `react-hot-loader/root` is not supported on your system. ' + 'Please use `import {hot} from "react-hot-loader"` instead' ); } // remove self from a cache delete cache[module.id]; // setup hot for caller exports.hot = hot(parent); } else { fallbackHot(); } } else { // prod mode fallbackHot(); } function fallbackHot() { exports.hot = function (a) { return a; }; } ================================================ FILE: scripts/ci.sh ================================================ set -e echo "Building project" yarn build echo "\n\n" echo "Linting" yarn lint echo "\n\n" ./scripts/react-16.ci.sh ./scripts/react-17.ci.sh ================================================ FILE: scripts/react-15.ci.sh ================================================ echo "Installing React 15" yarn add react@15 react-dom@15 react-test-renderer@15 --pure-lockfile echo "\n\n" yarn test:react-dom:prepare echo "Running tests on React 15 - Babel ES2015" yarn test:es2015 echo "\n\n" echo "Running tests on React 15 - Babel Modern" yarn test:modern echo "\n\n" echo "\n\n" ================================================ FILE: scripts/react-16.ci.sh ================================================ echo "Installing React 16.10" yarn add react@16 react-dom@16.10 react-test-renderer@16.10 --pure-lockfile echo "\n\n" yarn test:react-dom:prepare echo "Running tests on React 16.10 - Babel ES2015" yarn test:es2015 echo "\n\n" echo "Running tests on React 16.10 - Babel Modern" yarn test:modern --coverage && codecov echo "\n\n" echo "Installing React 16:latest" yarn add react@16 react-dom@16 react-test-renderer@16 --pure-lockfile echo "\n\n" yarn test:react-dom:prepare echo "Running tests on React 16:latest - Babel ES2015" yarn test:es2015 echo "\n\n" echo "Running tests on React 16:latest - Babel Modern" yarn test:modern --coverage && codecov echo "\n\n" ================================================ FILE: scripts/react-17.ci.sh ================================================ echo "Installing React 17" yarn add react@17.0.0-rc.2 react-dom@17.0.0-rc.2 react-test-renderer@17.0.0-rc.2 --pure-lockfile echo "\n\n" yarn test:react-dom:prepare echo "Running tests on React 17 - Babel ES2015" yarn test:es2015 echo "\n\n" echo "Running tests on React 17 - Babel Modern" yarn test:modern --coverage && codecov echo "\n\n" ================================================ FILE: src/AppContainer.dev.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import defaultPolyfill, { polyfill } from 'react-lifecycles-compat'; import logger from './logger'; import { get as getGeneration, hotComparisonOpen } from './global/generation'; import configuration from './configuration'; import { EmptyErrorPlaceholder, logException } from './errorReporter'; import { retryHotLoaderError } from './reconciler/proxyAdapter'; class AppContainer extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { if (prevState.generation !== getGeneration()) { // Hot reload is happening. return { error: null, generation: getGeneration(), }; } return null; } static reactHotLoadable = false; constructor(props) { super(props); if (configuration.showReactDomPatchNotification) { configuration.showReactDomPatchNotification = false; console.warn('React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.'); } this.state = { error: null, errorInfo: null, // eslint-disable-next-line react/no-unused-state generation: 0, }; } shouldComponentUpdate(prevProps, prevState) { // Don't update the component if the state had an error and still has one. // This allows to break an infinite loop of error -> render -> error -> render // https://github.com/gaearon/react-hot-loader/issues/696 if (prevState.error && this.state.error) { return false; } return true; } componentDidCatch(error, errorInfo) { logger.error(error); if (!hotComparisonOpen()) { // do not log error outside of HMR cycle // trigger update to kick error this.setState({}); throw error; } const { errorReporter = configuration.errorReporter } = this.props; if (!errorReporter) { logException(error, errorInfo, this); } this.setState({ error, errorInfo, }); } retryHotLoaderError() { this.setState({ error: null }, () => { retryHotLoaderError.call(this); }); } render() { const { error, errorInfo } = this.state; const { errorReporter: ErrorReporter = configuration.errorReporter || EmptyErrorPlaceholder } = this.props; if (error && this.props.errorBoundary) { return ; } if (this.hotComponentUpdate) { this.hotComponentUpdate(); } else { throw new Error('React-Hot-Loader: AppContainer should be patched'); } return React.Children.only(this.props.children); } } AppContainer.propTypes = { children(props) { if (React.Children.count(props.children) !== 1) { return new Error( 'Invalid prop "children" supplied to AppContainer. ' + 'Expected a single React element with your app’s root component, e.g. .', ); } return undefined; }, errorReporter: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), errorBoundary: PropTypes.bool, }; AppContainer.defaultProps = { errorBoundary: true, }; // trying first react-lifecycles-compat.polyfill, then trying react-lifecycles-compat, which could be .default const realPolyfill = polyfill || defaultPolyfill; realPolyfill(AppContainer); export default AppContainer; ================================================ FILE: src/AppContainer.prod.js ================================================ /* eslint-disable react/prop-types */ import React from 'react'; function AppContainer(props) { if (AppContainer.warnAboutHMRDisabled) { AppContainer.warnAboutHMRDisabled = true; console.error( 'React-Hot-Loader: misconfiguration detected, using production version in non-production environment.', ); console.error('React-Hot-Loader: Hot Module Replacement is not enabled.'); } return React.Children.only(props.children); } AppContainer.warnAboutHMRDisabled = false; export default AppContainer; ================================================ FILE: src/adapters/preact.js ================================================ import { setConfiguration } from '../configuration'; const tune = { allowSFC: false, }; export const preactAdapter = (instance, resolveType) => { const oldHandler = instance.options.vnode; setConfiguration(tune); instance.options.vnode = vnode => { if (vnode.type) { vnode.type = resolveType(vnode.type); } else if (vnode.nodeName) { vnode.nodeName = resolveType(vnode.nodeName); } if (oldHandler) { oldHandler(vnode); } }; }; ================================================ FILE: src/babel.dev.js ================================================ import { REGENERATE_METHOD } from './internal/constants'; import fresh from './fresh/babel'; const templateOptions = { placeholderPattern: /^([A-Z0-9]+)([A-Z0-9_]+)$/, }; /* eslint-disable */ const shouldIgnoreFile = file => !!file .split('\\') .join('/') .match(/node_modules\/(react|react-dom|react-hot-loader)([\/]|$)/); /* eslint-enable */ function plugin(args, options = {}) { // This is a Babel plugin, but the user put it in the Webpack config. if (this && this.callback) { throw new Error( 'React Hot Loader: You are erroneously trying to use a Babel plugin ' + 'as a Webpack loader. We recommend that you use Babel, ' + 'remove "react-hot-loader/babel" from the "loaders" section ' + 'of your Webpack configuration, and instead add ' + '"react-hot-loader/babel" to the "plugins" section of your .babelrc file. ' + 'If you prefer not to use Babel, replace "react-hot-loader/babel" with ' + '"react-hot-loader/webpack" in the "loaders" section of your Webpack configuration. ', ); } const { types: t, template } = args; const { safetyNet = true } = options; const buildRegistration = template('reactHotLoader.register(ID, NAME, FILENAME);', templateOptions); const signatureHeader = template( `var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {return a;}`, templateOptions, ); const headerTemplate = template( `(function () { var enterModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined); enterModule && enterModule(module); }())`, templateOptions, ); const footerTemplate = template( `(function () { var leaveModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined); leaveModule && leaveModule(module); }())`, templateOptions, ); const evalTemplate = template('this[key]=eval(code);', templateOptions); // We're making the IIFE we insert at the end of the file an unused variable // because it otherwise breaks the output of the babel-node REPL (#359). const buildTagger = template( ` (function () { var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined); if (!reactHotLoader) { return; } REGISTRATIONS }()); `, templateOptions, ); // Gather top-level variables, functions, and classes. // Try our best to avoid variables from require(). // Ideally we only want to find components defined by the user. function shouldRegisterBinding(binding) { const { type, node, parent } = binding.path; switch (type) { case 'FunctionDeclaration': case 'ClassDeclaration': case 'VariableDeclaration': return true; case 'VariableDeclarator': { const { init } = node; if (t.isCallExpression(init) && init.callee.name === 'require') { return false; } if (parent.declare) { return false; } return true; } default: return false; } } const REGISTRATIONS = Symbol('registrations'); return { visitor: { ExportDefaultDeclaration(path, state) { const { file } = state; // Default exports with names are going // to be in scope anyway so no need to bother. if (path.node.declaration.id) { return; } // Move export default right hand side to a variable // so we can later refer to it and tag it with __source. const id = path.scope.generateUidIdentifier('default'); const expression = t.isExpression(path.node.declaration) ? path.node.declaration : t.toExpression(path.node.declaration); path.scope.registerDeclaration( path.insertBefore(t.variableDeclaration('const', [t.variableDeclarator(id, expression)]))[0], ); path.node.declaration = id; // eslint-disable-line no-param-reassign // It won't appear in scope.bindings // so we'll manually remember it exists. state[REGISTRATIONS].push( buildRegistration({ ID: id, NAME: t.stringLiteral('default'), FILENAME: t.stringLiteral(file.opts.filename), }), ); }, Program: { enter({ scope, node }, state) { const { file } = state; state[REGISTRATIONS] = []; // eslint-disable-line no-param-reassign node.body.unshift(signatureHeader()); // Everything in the top level scope, when reasonable, // is going to get tagged with __source. /* eslint-disable guard-for-in,no-restricted-syntax */ for (const id in scope.bindings) { const binding = scope.bindings[id]; if (shouldRegisterBinding(binding)) { state[REGISTRATIONS].push( buildRegistration({ ID: binding.identifier, NAME: t.stringLiteral(id), FILENAME: t.stringLiteral(file.opts.filename), }), ); } } /* eslint-enable */ }, exit({ node }, state) { const { file } = state; const registrations = state[REGISTRATIONS]; state[REGISTRATIONS] = []; // inject the code only if applicable if (registrations && registrations.length && !shouldIgnoreFile(file.opts.filename)) { if (safetyNet) { node.body.unshift(headerTemplate()); } // Inject the generated tagging code at the very end // so that it is as minimally intrusive as possible. node.body.push(t.emptyStatement()); node.body.push(buildTagger({ REGISTRATIONS: registrations })); node.body.push(t.emptyStatement()); if (safetyNet) { node.body.push(footerTemplate()); } } }, }, Class(classPath) { const classBody = classPath.get('body'); let hasRegenerateMethod = false; let hasMethods = false; classBody.get('body').forEach(path => { const { node } = path; // don't apply transform to static class properties if (node.static) { return; } if (node.key.name !== REGENERATE_METHOD) { hasMethods = true; } else { hasRegenerateMethod = true; } }); if (hasMethods && !hasRegenerateMethod) { const regenerateMethod = t.classMethod( 'method', t.identifier(REGENERATE_METHOD), [t.identifier('key'), t.identifier('code')], t.blockStatement([evalTemplate()]), ); classBody.pushContainer('body', regenerateMethod); classBody.get('body').forEach(path => { const { node } = path; if (node.key.name === REGENERATE_METHOD) { path.addComment('leading', ' @ts-ignore', true); path .get('body') .get('body')[0] .addComment('leading', ' @ts-ignore', true); } }); } }, }, }; } const mergeRecord = (sourceRecord, newRecord) => { Object.keys(newRecord).forEach(key => { const action = newRecord[key]; if (typeof action === 'function') { if (!sourceRecord[key]) { sourceRecord[key] = () => ({}); } const prev = sourceRecord[key]; sourceRecord[key] = (...args) => { prev(...args); action(...args); }; } else if (typeof action === 'object') { if (!sourceRecord[key]) { sourceRecord[key] = {}; } mergeRecord(sourceRecord[key], action); } }); }; const composePlugins = plugins => (...args) => { const result = {}; plugins.forEach(creator => { const plugin = creator(...args); mergeRecord(result, plugin); }); return result; }; module.exports = composePlugins([ plugin, (...args) => { const p = fresh(...args); // removing everything we dont want right now // registration // delete p.visitor.Program; // delete p.visitor.Program.exit; // registrations // delete p.visitor.FunctionDeclaration.enter; // delete p.visitor.FunctionDeclaration.leave; // delete p.visitor.VariableDeclaration; return p; }, ]); module.exports.shouldIgnoreFile = shouldIgnoreFile; ================================================ FILE: src/babel.prod.js ================================================ const RHLPackage = 'react-hot-loader'; const RHLRootPackage = 'react-hot-loader/root'; const RHLPackages = [RHLPackage, RHLRootPackage]; function isImportedFromPackages(path, name, packages) { const binding = path.scope.getBinding(name); const bindingType = binding && binding.path.node.type; if (bindingType === 'ImportSpecifier' || bindingType === 'ImportNamespaceSpecifier') { const bindingParent = binding.path.parent; return packages.includes(bindingParent.source.value); } return false; } function isImportedFromRHL(path, name) { return isImportedFromPackages(path, name, [RHLPackage]); } function isImportedFromRHLRoot(path, name) { return isImportedFromPackages(path, name, [RHLRootPackage]); } function getRHLContext(file) { const context = []; const { body } = file.ast.program; for (let i = 0; i < body.length; i++) { const bodyItem = body[i]; const { source, specifiers } = bodyItem; if (bodyItem.type !== 'ImportDeclaration' || !RHLPackages.includes(source.value)) { continue; } for (let j = 0; j < specifiers.length; j++) { const specifier = specifiers[j]; if (specifier.type === 'ImportNamespaceSpecifier') { context.push({ kind: 'namespace', local: specifier.local.name, }); } else if (specifier.type === 'ImportSpecifier') { const specifierData = { kind: 'named', local: specifier.local.name, }; if (specifier.imported.name === 'hot') { context.push(specifierData); } } } } return context.length ? context : null; } export default function plugin() { function handleCall(path) { if (this.cancel) { return; } for (let i = 0; i < this.rhlContext.length; i++) { const specifier = this.rhlContext[i]; if (specifier.kind === 'named') { // replaces hot(module)(App) if ( path.node.callee.name === specifier.local && // ensure that this is `hot` from RHL isImportedFromRHL(path, specifier.local) && path.parent.type === 'CallExpression' && path.parent.arguments.length === 1 && path.parent.arguments[0] && path.parent.arguments[0].type === 'Identifier' ) { path.parentPath.replaceWith(path.parent.arguments[0]); break; } // replaces hot(App) if ( path.node.callee.name === specifier.local && // ensure that this is `hot` from RHL isImportedFromRHLRoot(path, specifier.local) && path.type === 'CallExpression' && path.node.arguments[0] && path.node.arguments[0].type === 'Identifier' ) { path.replaceWith(path.node.arguments[0]); break; } } else if (specifier.kind === 'namespace') { // replaces RHL.hot(module)(App) if ( path.node.callee.callee && path.node.callee.callee.type === 'MemberExpression' && path.node.callee.callee.object.type === 'Identifier' && path.node.callee.callee.object.name === specifier.local && // ensure that this is from RHL isImportedFromRHL(path, specifier.local) && path.node.callee.callee.property.type === 'Identifier' && path.node.callee.callee.property.name === 'hot' && path.node.arguments[0] && path.node.arguments[0].type === 'Identifier' ) { path.replaceWith(path.node.arguments[0]); break; } } } } return { pre() { // ignore files that do not use RHL this.rhlContext = getRHLContext(this.file); if (!this.rhlContext) { this.cancel = true; } }, visitor: { CallExpression: handleCall, }, }; } ================================================ FILE: src/configuration.js ================================================ const configuration = { // Log level logLevel: 'error', // Allows using SFC without changes pureSFC: true, // keep render method unpatched, moving sideEffect to componentDidUpdate pureRender: true, // Allows SFC to be used, enables "intermediate" components used by Relay, should be disabled for Preact allowSFC: true, // Allow reload of effect hooks with non zero dependency list reloadHooks: true, // Allow reload of mount effect hooks - zero deps reloadLifeCycleHooks: false, // Enables hook reload on hook body change reloadHooksOnBodyChange: true, // Disable "hot-replacement-render" disableHotRenderer: false, // @private integratedComparator: false, // @private integratedResolver: false, // Disable "hot-replacement-render" when injection into react-dom is made disableHotRendererWhenInjected: true, // Controls `react-🔥-dom patch` notification showReactDomPatchNotification: true, // Hook on babel component register. onComponentRegister: false, // Hook on React renders for a first time component onComponentCreate: false, // flag to completely disable RHL for SFC. Probably don't use it without dom patch made. ignoreSFC: false, // ignoreSFC when injection into react-dom is made ignoreSFCWhenInjected: true, // flag to completely disable RHL for Components ignoreComponents: false, // default value for AppContainer errorOverlay errorReporter: undefined, // Global error overlay ErrorOverlay: undefined, // Actively track lazy loaded components trackTailUpdates: true, // wrap lazy with AppProvider to allow independent updates wrapLazy: true, // react hot dom features enabled IS_REACT_MERGE_ENABLED: false, }; export const internalConfiguration = { // control proxy creation disableProxyCreation: false, }; export const setConfiguration = config => { // not using Object.assing for IE11 compliance for (const i in config) { if (config.hasOwnProperty(i)) { configuration[i] = config[i]; } } }; export default configuration; ================================================ FILE: src/errorReporter.js ================================================ /* global document */ /* eslint-disable react/no-array-index-key */ /* eslint-disable jsx-a11y/accessible-emoji */ /* eslint-disable no-use-before-define */ import React from 'react'; import ReactDom from 'react-dom'; import configuration from './configuration'; import { getComponentDisplayName } from './internal/reactUtils'; import { enterHotUpdate } from './global/generation'; let lastError = []; const overlayStyle = { position: 'fixed', left: 0, top: 0, right: 0, backgroundColor: 'rgba(255,200,200,0.9)', color: '#000', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif', fontSize: '12px', margin: 0, padding: '16px', maxHeight: '50%', overflow: 'auto', zIndex: 10000 }; const inlineErrorStyle = { backgroundColor: '#FEE', }; const liCounter = { position: 'absolute', left: '10px', }; const listStyle = {}; export const EmptyErrorPlaceholder = ({ component }) => ( ⚛️🔥🤕 ({component ? getComponentDisplayName(component.constructor || component) : 'Unknown location'}) {component && component.retryHotLoaderError && ( )} ); const errorHeader = (component, componentStack) => { if (component || componentStack) { return ( ( {component ? getComponentDisplayName(component.constructor || component) : 'Unknown location'} {component && ', '} {componentStack && componentStack.split('\n').filter(Boolean)[0]} ) ); } return null; }; const mapError = ({ error, errorInfo, component }) => { if (!error) { error = { message: 'undefined error' }; } return (

{errorHeader(component, errorInfo && errorInfo.componentStack)}{' '} {error.toString ? error.toString() : (error && error.message) || 'undefined error'}

{errorInfo && errorInfo.componentStack ? (
Stack trace:
    {error.stack .split('\n') .slice(1, 2) .map((line, i) =>
  • {line}
  • )}
    {errorInfo.componentStack .split('\n') .filter(Boolean) .map((line, i) =>
  • {line}
  • )}
) : ( error.stack && (
Stack trace:
    {error.stack.split('\n').map((line, i) =>
  • {line}
  • )}
) )}
); }; class ErrorOverlay extends React.Component { state = { visible: true, }; toggle = () => this.setState({ visible: !this.state.visible }); retry = () => this.setState(() => { const { errors } = this.props; enterHotUpdate(); clearExceptions(); errors .map(({ component }) => component) .filter(Boolean) .filter(({ retryHotLoaderError }) => !!retryHotLoaderError) .forEach(component => component.retryHotLoaderError()); return {}; }); render() { const { errors } = this.props; if (!errors.length) { return null; } const { visible } = this.state; return (

⚛️🔥😭: hot update was not successful

{visible && (
    {errors.map((err, i) => (
  • ({i + 1}/{errors.length}) {mapError(err)}
  • ))}
)}
); } } const initErrorOverlay = () => { if (typeof document === 'undefined' || !document.body) { return; } let div = document.querySelector('.react-hot-loader-error-overlay'); if (!div) { div = document.createElement('div'); div.className = 'react-hot-loader-error-overlay'; document.body.appendChild(div); } if (lastError.length) { const Overlay = configuration.ErrorOverlay || ErrorOverlay; ReactDom.render(, div); } else { div.parentNode.removeChild(div); } }; export function clearExceptions() { if (lastError.length) { lastError = []; initErrorOverlay(); } } export function logException(error, errorInfo, component) { // do not suppress error /* eslint-disable no-console */ console.error(error); /* eslint-enable */ lastError.push({ error, errorInfo, component }); initErrorOverlay(); } ================================================ FILE: src/fresh/babel.js ================================================ /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; const SIGNATURE = '__signature__'; export default function (babel) { const {types: t} = babel; const registrationsByProgramPath = new Map(); function createRegistration(programPath, persistentID) { const handle = programPath.scope.generateUidIdentifier('c'); if (!registrationsByProgramPath.has(programPath)) { registrationsByProgramPath.set(programPath, []); } const registrations = registrationsByProgramPath.get(programPath); registrations.push({ handle, persistentID, }); return handle; } function isComponentishName(name) { return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z'; } function findInnerComponents(inferredName, path, callback) { const node = path.node; switch (node.type) { case 'Identifier': { if (!isComponentishName(node.name)) { return false; } // export default hoc(Foo) // const X = hoc(Foo) callback(inferredName, node, null); return true; } case 'FunctionDeclaration': { // function Foo() {} // export function Foo() {} // export default function Foo() {} callback(inferredName, node.id, null); return true; } case 'ArrowFunctionExpression': { if (node.body.type === 'ArrowFunctionExpression') { return false; } // let Foo = () => {} // export default hoc1(hoc2(() => {})) callback(inferredName, node, path); return true; } case 'FunctionExpression': { // let Foo = function() {} // const Foo = hoc1(forwardRef(function renderFoo() {})) // export default memo(function() {}) callback(inferredName, node, path); return true; } case 'CallExpression': { const argsPath = path.get('arguments'); if (argsPath === undefined || argsPath.length === 0) { return false; } const calleePath = path.get('callee'); switch (calleePath.node.type) { case 'MemberExpression': case 'Identifier': { const calleeSource = calleePath.getSource(); const firstArgPath = argsPath[0]; const innerName = inferredName + '$' + calleeSource; const foundInside = findInnerComponents( innerName, firstArgPath, callback, ); if (!foundInside) { return false; } // const Foo = hoc1(hoc2(() => {})) // export default memo(React.forwardRef(function() {})) callback(inferredName, node, path); return true; } default: { return false; } } } case 'VariableDeclarator': { const init = node.init; if (init === null) { return false; } const name = node.id.name; if (!isComponentishName(name)) { return false; } if (init.type === 'Identifier' || init.type === 'MemberExpression') { return false; } const initPath = path.get('init'); const foundInside = findInnerComponents( inferredName, initPath, callback, ); if (foundInside) { return true; } // See if this identifier is used in JSX. Then it's a component. const binding = path.scope.getBinding(name); if (binding === undefined) { return; } let isLikelyUsedAsType = false; const referencePaths = binding.referencePaths; for (let i = 0; i < referencePaths.length; i++) { const ref = referencePaths[i]; if ( ref.node.type !== 'JSXIdentifier' && ref.node.type !== 'Identifier' ) { continue; } const refParent = ref.parent; if (refParent.type === 'JSXOpeningElement') { isLikelyUsedAsType = true; } else if (refParent.type === 'CallExpression') { const callee = refParent.callee; let fnName; switch (callee.type) { case 'Identifier': fnName = callee.name; break; case 'MemberExpression': fnName = callee.property.name; break; } switch (fnName) { case 'createElement': case 'jsx': case 'jsxDEV': case 'jsxs': isLikelyUsedAsType = true; break; } } if (isLikelyUsedAsType) { // const X = ... + later callback(inferredName, init, initPath); return true; } } } } return false; } function isBuiltinHook(hookName) { switch (hookName) { case 'useState': case 'React.useState': case 'useReducer': case 'React.useReducer': case 'useEffect': case 'React.useEffect': case 'useLayoutEffect': case 'React.useLayoutEffect': case 'useMemo': case 'React.useMemo': case 'useCallback': case 'React.useCallback': case 'useRef': case 'React.useRef': case 'useContext': case 'React.useContext': case 'useImperativeMethods': case 'React.useImperativeMethods': case 'useDebugValue': case 'React.useDebugValue': return true; default: return false; } } function getCalleeName(callee) { if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier') { return callee.object.name; } return callee.name; } function getHookCallsSignature(functionNode, scope) { const fnHookCalls = hookCalls.get(functionNode); if (fnHookCalls === undefined) { return null; } return { key: fnHookCalls.map(call => call.name + '{' + call.key + '}').join('\n'), customHooks: fnHookCalls .filter(call => !isBuiltinHook(call.name)) .filter(call => scope.parent.hasBinding(call.name)) .map(call => t.cloneDeep(call.callee)), }; } function createArgumentsForSignature(node, signature, scope) { const {key, customHooks} = signature; const args = [node, t.stringLiteral(key)]; const hooksInScope = customHooks.filter(call => scope.hasBinding(getCalleeName(call))); if (hooksInScope.length > 0) { args.push(t.arrowFunctionExpression([], t.arrayExpression(hooksInScope))); } return args; } let seenForRegistration = new WeakSet(); let seenForSignature = new WeakSet(); let hookCalls = new WeakMap(); const HookCallsVisitor = { CallExpression(path) { const node = path.node; const callee = node.callee; // Note: this visitor MUST NOT mutate the tree in any way. // It runs early in a separate traversal and should be very fast. let name = null; switch (callee.type) { case 'Identifier': name = callee.name; break; case 'MemberExpression': name = callee.property.name; break; } if (name === null || !/^use[A-Z]/.test(name)) { return; } const fnScope = path.scope.getFunctionParent(); if (fnScope === null) { return; } // This is a Hook call. Record it. const fnNode = fnScope.block; if (!hookCalls.has(fnNode)) { hookCalls.set(fnNode, []); } let hookCallsForFn = hookCalls.get(fnNode); let key = ''; if (path.parent.type === 'VariableDeclarator') { // TODO: if there is no LHS, consider some other heuristic. key = path.parentPath.get('id').getSource(); } // Some built-in Hooks reset on edits to arguments. const args = path.get('arguments'); if (name === 'useState' && args.length > 0) { // useState second argument is initial state. key += '(' + args[0].getSource() + ')'; } else if (name === 'useReducer' && args.length > 1) { // useReducer second argument is initial state. key += '(' + args[1].getSource() + ')'; } hookCallsForFn.push({ callee: path.node.callee, name, key, }); }, }; return { visitor: { ExportDefaultDeclaration(path) { const node = path.node; const decl = node.declaration; const declPath = path.get('declaration'); if (decl.type !== 'CallExpression') { // For now, we only support possible HOC calls here. // Named function declarations are handled in FunctionDeclaration. // Anonymous direct exports like export default function() {} // are currently ignored. return; } // Make sure we're not mutating the same tree twice. // This can happen if another Babel plugin replaces parents. if (seenForRegistration.has(node)) { return; } seenForRegistration.add(node); // Don't mutate the tree above this point. // This code path handles nested cases like: // export default memo(() => {}) // In those cases it is more plausible people will omit names // so they're worth handling despite possible false positives. // More importantly, it handles the named case: // export default memo(function Named() {}) const inferredName = '%default%'; const programPath = path.parentPath; findInnerComponents( inferredName, declPath, (persistentID, targetExpr, targetPath) => { if (targetPath === null) { // For case like: // export default hoc(Foo) // we don't want to wrap Foo inside the call. // Instead we assume it's registered at definition. return; } const handle = createRegistration(programPath, persistentID); targetPath.replaceWith( t.assignmentExpression('=', handle, targetExpr), ); }, ); }, FunctionDeclaration: { enter(path) { return; const node = path.node; let programPath; let insertAfterPath; switch (path.parent.type) { case 'Program': insertAfterPath = path; programPath = path.parentPath; break; case 'ExportNamedDeclaration': insertAfterPath = path.parentPath; programPath = insertAfterPath.parentPath; break; case 'ExportDefaultDeclaration': insertAfterPath = path.parentPath; programPath = insertAfterPath.parentPath; break; default: return; } const id = node.id; if (id === null) { // We don't currently handle anonymous default exports. return; } const inferredName = id.name; if (!isComponentishName(inferredName)) { return; } // Make sure we're not mutating the same tree twice. // This can happen if another Babel plugin replaces parents. if (seenForRegistration.has(node)) { return; } seenForRegistration.add(node); // Don't mutate the tree above this point. // export function Named() {} // function Named() {} findInnerComponents( inferredName, path, (persistentID, targetExpr) => { const handle = createRegistration(programPath, persistentID); insertAfterPath.insertAfter( t.expressionStatement( t.assignmentExpression('=', handle, targetExpr), ), ); }, ); }, exit(path) { //return; const node = path.node; const id = node.id; if (id === null) { return; } const signature = getHookCallsSignature(node, path.scope); if (signature === null) { return; } // Make sure we're not mutating the same tree twice. // This can happen if another Babel plugin replaces parents. if (seenForSignature.has(node)) { return; } seenForSignature.add(node); // Unlike with __register__, this needs to work for nested // declarations too. So we need to search for a path where // we can insert a statement rather than hardcoding it. let insertAfterPath = null; path.find(p => { if (p.parentPath.isBlock()) { insertAfterPath = p; return true; } }); if (insertAfterPath === null) { return; } insertAfterPath.insertAfter( t.expressionStatement( t.callExpression( t.identifier(SIGNATURE), createArgumentsForSignature(id, signature, insertAfterPath.scope), ), ), ); }, }, 'ArrowFunctionExpression|FunctionExpression': { exit(path) { const node = path.node; const signature = getHookCallsSignature(node, path.scope); if (signature === null) { return; } // Make sure we're not mutating the same tree twice. // This can happen if another Babel plugin replaces parents. if (seenForSignature.has(node)) { return; } seenForSignature.add(node); // Don't mutate the tree above this point. if (path.parent.type === 'VariableDeclarator') { let insertAfterPath = null; path.find(p => { if (p.parentPath.isBlock()) { insertAfterPath = p; return true; } }); if (insertAfterPath === null) { return; } // Special case when a function would get an inferred name: // let Foo = () => {} // let Foo = function() {} // We'll add signature it on next line so that // we don't mess up the inferred 'Foo' function name. insertAfterPath.insertAfter( t.expressionStatement( t.callExpression( t.identifier(SIGNATURE), createArgumentsForSignature(path.parent.id, signature, insertAfterPath.scope), ), ), ); // Result: let Foo = () => {}; __signature(Foo, ...); } else { // let Foo = hoc(() => {}) path.replaceWith( t.callExpression( t.identifier(SIGNATURE), createArgumentsForSignature(node, signature, path.scope), ), ); // Result: let Foo = hoc(__signature(() => {}, ...)) } }, }, Program: { enter(path) { // This is a separate early visitor because we need to collect Hook calls // and "const [foo, setFoo] = ..." signatures before the destructuring // transform mangles them. This extra traversal is not ideal for perf, // but it's the best we can do until we stop transpiling destructuring. path.traverse(HookCallsVisitor); }, }, }, }; } ================================================ FILE: src/global/generation.js ================================================ import { forEachKnownClass } from '../proxy/createClassProxy'; // this counter tracks `register` invocations. // works good, but code splitting is breaking it let generation = 1; // these counters are aimed to mitigate the "first render" let hotComparisonCounter = 0; let hotComparisonRuns = 0; let hotReplacementGeneration = 0; const nullFunction = () => ({}); // these callbacks would be called on component update let onHotComparisonOpen = nullFunction; let onHotComparisonElement = nullFunction; let onHotComparisonClose = nullFunction; // inversion of control export const setComparisonHooks = (open, element, close) => { onHotComparisonOpen = open; onHotComparisonElement = element; onHotComparisonClose = close; }; export const getElementComparisonHook = component => onHotComparisonElement(component); export const getElementCloseHook = component => onHotComparisonClose(component); export const hotComparisonOpen = () => hotComparisonCounter > 0 && hotComparisonRuns > 0 && hotReplacementGeneration > 0; export const openGeneration = () => forEachKnownClass(onHotComparisonElement); export const closeGeneration = () => forEachKnownClass(onHotComparisonClose); const incrementHot = () => { if (!hotComparisonCounter) { openGeneration(); onHotComparisonOpen(); } hotComparisonCounter++; }; const decrementHot = () => { hotComparisonCounter--; if (!hotComparisonCounter) { closeGeneration(); hotComparisonRuns++; } }; export const configureGeneration = (counter, runs) => { hotComparisonCounter = counter; hotComparisonRuns = runs; hotReplacementGeneration = runs; }; // TODO: shall it be called from incrementHotGeneration? export const enterHotUpdate = () => { Promise.resolve(incrementHot()).then(() => setTimeout(decrementHot, 0)); }; // TODO: deprecate? export const increment = () => { enterHotUpdate(); return generation++; }; export const get = () => generation; // These counters tracks HMR generations, and probably should be used instead of the old one export const incrementHotGeneration = () => hotReplacementGeneration++; export const getHotGeneration = () => hotReplacementGeneration; ================================================ FILE: src/global/modules.js ================================================ import logger from '../logger'; const openedModules = {}; let lastModuleOpened = ''; export const getLastModuleOpened = () => lastModuleOpened; const hotModules = {}; const createHotModule = () => ({ instances: [], updateTimeout: 0 }); export const hotModule = moduleId => { if (!hotModules[moduleId]) { hotModules[moduleId] = createHotModule(); } return hotModules[moduleId]; }; export const isOpened = sourceModule => sourceModule && !!openedModules[sourceModule.id]; export const enter = sourceModule => { if (sourceModule && sourceModule.id) { lastModuleOpened = sourceModule.id; openedModules[sourceModule.id] = true; } else { logger.warn('React-hot-loader: no `module` variable found. Did you shadow a system variable?'); } }; export const leave = sourceModule => { if (sourceModule && sourceModule.id) { delete openedModules[sourceModule.id]; } }; ================================================ FILE: src/hot.dev.js ================================================ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import hoistNonReactStatic from 'hoist-non-react-statics'; import { getComponentDisplayName } from './internal/reactUtils'; import configuration from './configuration'; import AppContainer from './AppContainer.dev'; import reactHotLoader from './reactHotLoader'; import { isOpened as isModuleOpened, hotModule, getLastModuleOpened } from './global/modules'; import logger from './logger'; import { clearExceptions, logException } from './errorReporter'; import { createQueue } from './utils/runQueue'; import { enterHotUpdate, getHotGeneration, increment } from './global/generation'; /* eslint-disable camelcase, no-undef */ const requireIndirect = typeof __webpack_require__ !== 'undefined' ? __webpack_require__ : require; /* eslint-enable */ const chargeFailbackTimer = id => setTimeout(() => { const error = `hot update failed for module "${id}". Last file processed: "${getLastModuleOpened()}".`; logger.error(error); logException({ toString: () => error, }); // 100 ms more "code" tolerant that 0, and would catch error in any case }, 100); const clearFailbackTimer = timerId => clearTimeout(timerId); const createHoc = (SourceComponent, TargetComponent) => { hoistNonReactStatic(TargetComponent, SourceComponent); TargetComponent.displayName = `HotExported${getComponentDisplayName(SourceComponent)}`; return TargetComponent; }; const runInRequireQueue = createQueue(); const runInRenderQueue = createQueue(cb => { if (ReactDOM.unstable_batchedUpdates) { ReactDOM.unstable_batchedUpdates(cb); } else { cb(); } }); const makeHotExport = (sourceModule, moduleId) => { const updateInstances = possibleError => { if (possibleError && possibleError instanceof Error) { console.error(possibleError); return; } const module = hotModule(moduleId); const deepUpdate = () => { // force flush all updates runInRenderQueue(() => { enterHotUpdate(); const gen = getHotGeneration(); module.instances.forEach(inst => inst.forceUpdate()); if (configuration.trackTailUpdates) { let runLimit = 0; const checkTailUpdates = () => { setTimeout(() => { if (getHotGeneration() !== gen) { // we know that some components were updated, but not tracking which ones // even if their updates might be incorporated automatically (like lazy) // we dont know which one should be tracked, and which updates are important logger.warn( 'React-Hot-Loader: some components were updated out-of-bound. Updating your app to reconcile the changes.', ); // increment generator for cache-busting existing tree increment(); deepUpdate(); } else if (++runLimit < 5) { checkTailUpdates(); } }, 16); }; checkTailUpdates(); } }); }; // require all modules runInRequireQueue(() => { try { // webpack will require everything by this time // but let's double check... requireIndirect(moduleId); } catch (e) { console.error('React-Hot-Loader: error detected while loading', moduleId); console.error(e); } }).then(deepUpdate); }; if (sourceModule.hot) { // Mark as self-accepted for Webpack (callback is an Error Handler) // Update instances for Parcel (callback is an Accept Handler) sourceModule.hot.accept(updateInstances); // Webpack way if (sourceModule.hot.addStatusHandler) { if (sourceModule.hot.status() === 'idle') { sourceModule.hot.addStatusHandler(status => { if (status === 'apply') { clearExceptions(); updateInstances(); } }); } } } else { logger.warn('React-hot-loader: Hot Module Replacement is not enabled'); } }; const hot = sourceModule => { if (!sourceModule) { // this is fatal throw new Error('React-hot-loader: `hot` was called without any argument provided'); } const moduleId = sourceModule.id || sourceModule.i || sourceModule.filename; if (!moduleId) { console.error('`module` provided', sourceModule); throw new Error('React-hot-loader: `hot` could not find the `name` of the the `module` you have provided'); } const module = hotModule(moduleId); makeHotExport(sourceModule, moduleId); clearExceptions(); const failbackTimer = chargeFailbackTimer(moduleId); let firstHotRegistered = false; // TODO: Ensure that all exports from this file are react components. return (WrappedComponent, props) => { clearFailbackTimer(failbackTimer); // register proxy for wrapped component // only one hot per file would use this registration if (!firstHotRegistered) { firstHotRegistered = true; reactHotLoader.register(WrappedComponent, getComponentDisplayName(WrappedComponent), `RHL${moduleId}`); } return createHoc( WrappedComponent, class ExportedComponent extends Component { componentDidMount() { module.instances.push(this); } componentWillUnmount() { if (isModuleOpened(sourceModule)) { const componentName = getComponentDisplayName(WrappedComponent); logger.error( `React-hot-loader: Detected AppContainer unmount on module '${moduleId}' update.\n` + `Did you use "hot(${componentName})" and "ReactDOM.render()" in the same file?\n` + `"hot(${componentName})" shall only be used as export.\n` + `Please refer to "Getting Started" (https://github.com/gaearon/react-hot-loader/).`, ); } module.instances = module.instances.filter(a => a !== this); } render() { return ( ); } }, ); }; }; reactHotLoader.register(AppContainer, 'AppContainer', 'hot-dev'); export default hot; ================================================ FILE: src/hot.prod.js ================================================ import React from 'react'; import AppContainer from './AppContainer.prod'; const hot = () => { if (hot.shouldWrapWithAppContainer) { return Component => props => ( ); } return Component => Component; }; hot.shouldWrapWithAppContainer = false; export default hot; ================================================ FILE: src/index.dev.js ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import ReactHotLoader from './reactHotLoader'; import './reconciler/proxyAdapter'; export { default as AppContainer } from './AppContainer.dev'; export { default as hot } from './hot.dev'; export { enter as enterModule, leave as leaveModule } from './global/modules'; export * from './utils.dev'; export default ReactHotLoader; ReactHotLoader.patch(React, ReactDOM); ================================================ FILE: src/index.prod.js ================================================ export { default as AppContainer } from './AppContainer.prod'; export { default as hot } from './hot.prod'; export * from './utils.prod'; ================================================ FILE: src/internal/constants.js ================================================ // the same as before const PREFIX = '__reactstandin__'; export const REGENERATE_METHOD = `${PREFIX}regenerateByEval`; ================================================ FILE: src/internal/getReactStack.js ================================================ /* eslint-disable no-underscore-dangle */ import ReactDOM from 'react-dom'; import hydrateFiberStack from './stack/hydrateFiberStack'; import hydrateLegacyStack from './stack/hydrateLegacyStack'; import { getInternalInstance } from './reactUtils'; import { resolveType } from '../reconciler/resolver'; function getReactStack(instance) { const rootNode = getInternalInstance(instance); const stack = {}; if (rootNode) { // React stack const isFiber = typeof rootNode.tag === 'number'; if (isFiber) { hydrateFiberStack(rootNode, stack); } else { hydrateLegacyStack(rootNode, stack); } } else { // Non-React stack // preact? // inferno? // there is no known VDOM to get stack from } return stack; } const markUpdate = ({ fiber }) => { // do not update what we should not if (!fiber || typeof fiber.type === 'string') { return; } const mostResentType = resolveType(fiber.type) || fiber.type; fiber.type = mostResentType; // do not change fiber.elementType to keep old information for the hot-update fiber.expirationTime = 1; if (fiber.alternate) { fiber.alternate.expirationTime = 1; fiber.alternate.type = fiber.type; } if (fiber.memoizedProps && typeof fiber.memoizedProps === 'object') { fiber.memoizedProps = { cacheBusterProp: true, ...fiber.memoizedProps, }; } if (fiber.stateNode) { // TODO: this might work better React 16, but breaking tests for React 15 changing "updates" counts. // updateInstance(fiber.stateNode); } }; export const cleanupReact = () => { if (ReactDOM.hotCleanup) { ReactDOM.hotCleanup(); } }; export const deepMarkUpdate = stack => { markUpdate(stack); if (stack.children) { stack.children.forEach(deepMarkUpdate); } }; export default getReactStack; ================================================ FILE: src/internal/reactUtils.js ================================================ import React from 'react'; /* eslint-disable no-underscore-dangle */ export const isCompositeComponent = type => typeof type === 'function'; export const isReloadableComponent = type => typeof type === 'function' || typeof type === 'object'; export const getComponentDisplayName = type => { const displayName = type.displayName || type.name; return displayName && displayName !== 'ReactComponent' ? displayName : 'Component'; }; export const reactLifeCycleMountMethods = ['componentWillMount', 'componentDidMount']; export function isReactClass(Component) { return !!( Component.prototype && (React.Component.prototype.isPrototypeOf(Component.prototype) || // react 14 support Component.prototype.isReactComponent || Component.prototype.componentWillMount || Component.prototype.componentWillUnmount || Component.prototype.componentDidMount || Component.prototype.componentDidUnmount || Component.prototype.render) ); } export function isReactClassInstance(Component) { return Component && isReactClass({ prototype: Object.getPrototypeOf(Component) }); } export const getInternalInstance = instance => instance._reactInternalFiber || // React 16 instance._reactInternalInstance || // React 15 null; export const updateInstance = instance => { const { updater, forceUpdate } = instance; if (typeof forceUpdate === 'function') { instance.forceUpdate(); } else if (updater && typeof updater.enqueueForceUpdate === 'function') { updater.enqueueForceUpdate(instance); } }; export const isFragmentNode = ({ type }) => React.Fragment && type === React.Fragment; const ContextType = React.createContext ? React.createContext() : null; const ConsumerType = ContextType && ContextType.Consumer.$$typeof; const ProviderType = ContextType && ContextType.Provider.$$typeof; const MemoType = React.memo && React.memo(() => null).$$typeof; const LazyType = React.lazy && React.lazy(() => null).$$typeof; const ForwardType = React.forwardRef && React.forwardRef(() => null).$$typeof; export const CONTEXT_CURRENT_VALUE = '_currentValue'; export const isContextConsumer = ({ type }) => type && typeof type === 'object' && '$$typeof' in type && type.$$typeof === ConsumerType && ConsumerType; export const isContextProvider = ({ type }) => type && typeof type === 'object' && '$$typeof' in type && type.$$typeof === ProviderType && ProviderType; export const isMemoType = ({ type }) => type && typeof type === 'object' && '$$typeof' in type && type.$$typeof === MemoType && MemoType; export const isLazyType = ({ type }) => type && typeof type === 'object' && '$$typeof' in type && type.$$typeof === LazyType && LazyType; export const isForwardType = ({ type }) => type && typeof type === 'object' && '$$typeof' in type && type.$$typeof === ForwardType && ForwardType; export const isContextType = type => isContextConsumer(type) || isContextProvider(type); export const getElementType = type => { const element = { type }; if (isContextConsumer(element)) { return 'Consumer'; } if (isContextProvider(element)) { return 'Provider'; } if (isLazyType(element)) { return 'Lazy'; } if (isMemoType(element)) { return 'Memo'; } if (isForwardType(element)) { return 'Forward'; } if (isReactClass(type)) { return 'Class'; } if (typeof element === 'function') { return 'FC'; } return 'unknown'; }; export const getContextProvider = type => type && type._context; ================================================ FILE: src/internal/stack/hydrateFiberStack.js ================================================ /* eslint-disable no-underscore-dangle */ import ReactDOM from 'react-dom'; export const hotRenderWithHooks = ReactDOM.hotRenderWithHooks || ((fiber, render) => render()); function pushStack(stack, node) { stack.type = node.type; stack.elementType = node.elementType || node.type; stack.children = []; stack.instance = typeof node.type === 'function' ? node.stateNode : stack; stack.fiber = node; if (!stack.instance) { stack.instance = { SFC_fake: stack.type, props: {}, render: () => hotRenderWithHooks(node, () => stack.type(stack.instance.props)), }; } } function hydrateFiberStack(node, stack) { pushStack(stack, node); if (node.child) { let { child } = node; do { const childStack = {}; hydrateFiberStack(child, childStack); stack.children.push(childStack); child = child.sibling; } while (child); } } export default hydrateFiberStack; ================================================ FILE: src/internal/stack/hydrateLegacyStack.js ================================================ /* eslint-disable no-underscore-dangle */ function pushState(stack, type, instance) { stack.type = type; stack.elementType = type; stack.children = []; stack.instance = instance || stack; if (typeof type === 'function' && type.isStatelessFunctionalProxy) { // In React 15 SFC is wrapped by component. We have to detect our proxies and change the way it works stack.instance = { SFC_fake: type, props: {}, render: () => type(stack.instance.props), }; } } function hydrateLegacyStack(node, stack) { if (node._currentElement) { pushState(stack, node._currentElement.type, node._instance || stack); } if (node._renderedComponent) { const childStack = {}; hydrateLegacyStack(node._renderedComponent, childStack); stack.children.push(childStack); } else if (node._renderedChildren) { Object.keys(node._renderedChildren).forEach(key => { const childStack = {}; hydrateLegacyStack(node._renderedChildren[key], childStack); stack.children.push(childStack); }); } } export default hydrateLegacyStack; ================================================ FILE: src/logger.js ================================================ /* eslint-disable no-console */ import configuration from './configuration'; const logger = { debug(...args) { if (['debug'].indexOf(configuration.logLevel) !== -1) { console.debug(...args); } }, log(...args) { if (['debug', 'log'].indexOf(configuration.logLevel) !== -1) { console.log(...args); } }, warn(...args) { if (['debug', 'log', 'warn'].indexOf(configuration.logLevel) !== -1) { console.warn(...args); } }, error(...args) { if (['debug', 'log', 'warn', 'error'].indexOf(configuration.logLevel) !== -1) { console.error(...args); } }, }; export default logger; ================================================ FILE: src/proxy/constants.js ================================================ export const PREFIX = '__reactstandin__'; export const PROXY_KEY = `${PREFIX}key`; export const GENERATION = `${PREFIX}proxyGeneration`; export const REGENERATE_METHOD = `${PREFIX}regenerateByEval`; export const UNWRAP_PROXY = `${PREFIX}getCurrent`; export const CACHED_RESULT = `${PREFIX}cachedResult`; export const PROXY_IS_MOUNTED = `${PREFIX}isMounted`; export const RENDERED_GENERATION = 'REACT_HOT_LOADER_RENDERED_GENERATION'; ================================================ FILE: src/proxy/createClassProxy.js ================================================ import { Component } from 'react'; import transferStaticProps from './transferStaticProps'; import { GENERATION, PROXY_KEY, UNWRAP_PROXY, CACHED_RESULT, PROXY_IS_MOUNTED, PREFIX, RENDERED_GENERATION, } from './constants'; import { identity, safeDefineProperty, proxyClassCreator } from './utils'; import { inject, checkLifeCycleMethods, mergeComponents } from './inject'; import config from '../configuration'; import { getComponentDisplayName, isReactClass, isReactClassInstance } from '../internal/reactUtils'; import { get as getGeneration, getElementCloseHook, getElementComparisonHook } from '../global/generation'; const has = Object.prototype.hasOwnProperty; let proxies = new WeakMap(); export const resetClassProxies = () => { proxies = new WeakMap(); }; const blackListedClassMembers = [ 'constructor', 'render', 'componentWillMount', 'componentDidMount', 'componentDidCatch', 'componentWillReceiveProps', 'componentWillUnmount', 'hotComponentRender', 'getInitialState', 'getDefaultProps', ]; const defaultRenderOptions = { componentWillRender: identity, componentDidUpdate: result => result, componentDidRender: result => result, }; const filteredPrototypeMethods = Proto => Object.getOwnPropertyNames(Proto).filter(prop => { const descriptor = Object.getOwnPropertyDescriptor(Proto, prop); return ( descriptor && prop.indexOf(PREFIX) !== 0 && blackListedClassMembers.indexOf(prop) < 0 && typeof descriptor.value === 'function' ); }); const defineClassMember = (Class, methodName, methodBody) => safeDefineProperty(Class.prototype, methodName, { configurable: true, writable: true, enumerable: false, value: methodBody, }); const defineClassMembers = (Class, methods) => Object.keys(methods).forEach(methodName => defineClassMember(Class, methodName, methods[methodName])); const setSFPFlag = (component, flag) => safeDefineProperty(component, 'isStatelessFunctionalProxy', { configurable: false, writable: false, enumerable: false, value: flag, }); const copyMethodDescriptors = (target, source) => { if (source) { // it is possible to use `function-double` to construct an ideal clone, but does not make a sence const keys = Object.getOwnPropertyNames(source); keys.forEach(key => safeDefineProperty(target, key, Object.getOwnPropertyDescriptor(source, key))); safeDefineProperty(target, 'toString', { configurable: true, writable: false, enumerable: false, value: function toString() { return String(source); }, }); } return target; }; const knownClassComponents = []; export const forEachKnownClass = cb => knownClassComponents.forEach(cb); function createClassProxy(InitialComponent, proxyKey, options = {}) { const renderOptions = { ...defaultRenderOptions, ...options, }; const proxyConfig = { ...config, ...options.proxy, }; // Prevent double wrapping. // Given a proxy class, return the existing proxy managing it. const existingProxy = proxies.get(InitialComponent); if (existingProxy) { return existingProxy; } let CurrentComponent; let savedDescriptors = {}; let injectedMembers = {}; let proxyGeneration = 0; let classUpdatePostponed = null; let instancesCount = 0; let isFunctionalComponent = !isReactClass(InitialComponent); let lastInstance = null; function postConstructionAction() { this[GENERATION] = 0; lastInstance = this; // is there is an update pending if (classUpdatePostponed) { const callUpdate = classUpdatePostponed; classUpdatePostponed = null; callUpdate(); } // As long we can't override constructor // every class shall evolve from a base class inject(this, proxyGeneration, injectedMembers); } function proxiedUpdate() { if (this) { inject(this, proxyGeneration, injectedMembers); } } function lifeCycleWrapperFactory(wrapperName, sideEffect = identity) { return copyMethodDescriptors(function wrappedMethod(...rest) { proxiedUpdate.call(this); sideEffect(this); return ( !isFunctionalComponent && CurrentComponent.prototype[wrapperName] && CurrentComponent.prototype[wrapperName].apply(this, rest) ); }, InitialComponent.prototype && InitialComponent.prototype[wrapperName]); } function methodWrapperFactory(wrapperName, realMethod) { return copyMethodDescriptors(function wrappedMethod(...rest) { return realMethod.apply(this, rest); }, realMethod); } const fakeBasePrototype = Proto => filteredPrototypeMethods(Proto).reduce((acc, key) => { acc[key] = methodWrapperFactory(key, Proto[key]); return acc; }, {}); const componentDidMount = lifeCycleWrapperFactory('componentDidMount', target => { target[PROXY_IS_MOUNTED] = true; target[RENDERED_GENERATION] = getGeneration(); instancesCount++; }); const componentDidUpdate = lifeCycleWrapperFactory('componentDidUpdate', renderOptions.componentDidUpdate); const componentWillUnmount = lifeCycleWrapperFactory('componentWillUnmount', target => { target[PROXY_IS_MOUNTED] = false; instancesCount--; }); function hotComponentRender() { // repeating subrender call to keep RENDERED_GENERATION up to date renderOptions.componentWillRender(this); proxiedUpdate.call(this); let result; // We need to use hasOwnProperty here, as the cached result is a React node // and can be null or some other falsy value. if (has.call(this, CACHED_RESULT)) { result = this[CACHED_RESULT]; delete this[CACHED_RESULT]; } else if (isFunctionalComponent) { result = CurrentComponent(this.props, this.context); } else { const renderMethod = CurrentComponent.prototype.render || this.render; /* eslint-disable no-use-before-define */ if (renderMethod === proxiedRender) { throw new Error('React-Hot-Loader: you are trying to render Component without .render method'); } /* eslint-enable */ result = renderMethod.apply( this, // eslint-disable-next-line prefer-rest-params arguments, ); } return renderOptions.componentDidRender.call(this, result); } function hotComponentUpdate() { renderOptions.componentWillRender(this); proxiedUpdate.call(this); } function proxiedRender(...args) { renderOptions.componentWillRender(this); return hotComponentRender.call(this, ...args); } const defineProxyMethods = (Proxy, Base = {}) => { defineClassMembers(Proxy, { ...fakeBasePrototype(Base), // eslint-disable-next-line no-nested-ternary ...(proxyConfig.pureRender ? {} : { render: proxiedRender }), hotComponentRender, hotComponentUpdate, componentDidMount, componentDidUpdate, componentWillUnmount, }); }; let ProxyFacade; let ProxyComponent = null; let proxy; if (!isFunctionalComponent) { // Component ProxyComponent = proxyClassCreator(InitialComponent, postConstructionAction); defineProxyMethods(ProxyComponent, InitialComponent.prototype); knownClassComponents.push(ProxyComponent); ProxyFacade = ProxyComponent; } else if (!proxyConfig.allowSFC) { proxyConfig.pureRender = false; // SFC Converted to component. Does not support returning precreated instances from render. ProxyComponent = proxyClassCreator(Component, postConstructionAction); defineProxyMethods(ProxyComponent); ProxyFacade = ProxyComponent; } else { // SFC // This function only gets called for the initial mount. The actual // rendered component instance will be the return value. // eslint-disable-next-line func-names ProxyFacade = function(props, context) { /* ! THIS IS NOT YOUR COMPONENT ! ! THIS IS REACT-HOT-LOADER ! And you are probably looking for a function component of yours It's hidden, but there is a way to fix this - just reconfigure your application a bit see https://github.com/gaearon/react-hot-loader/issues/1311 */ const result = CurrentComponent(props, context); // This is a Relay-style container constructor. We can't do the prototype- // style wrapping for this as we do elsewhere, so just we just pass it // through as-is. if (isReactClassInstance(result)) { ProxyComponent = null; // Relay lazily sets statics like getDerivedStateFromProps on initial // render in lazy construction, so we need to do the same here. transferStaticProps(ProxyFacade, savedDescriptors, null, CurrentComponent); return result; } // simple SFC, could continue to be SFC if (proxyConfig.pureSFC) { if (!CurrentComponent.contextTypes) { if (!ProxyFacade.isStatelessFunctionalProxy) { setSFPFlag(ProxyFacade, true); } return renderOptions.componentDidRender(result); } } setSFPFlag(ProxyFacade, false); proxyConfig.pureRender = false; // Otherwise, it's a normal functional component. Build the real proxy // and use it going forward. ProxyComponent = proxyClassCreator(Component, postConstructionAction); defineProxyMethods(ProxyComponent); const determinateResult = new ProxyComponent(props, context); // Cache the initial render result so we don't call the component function // a second time for the initial render. determinateResult[CACHED_RESULT] = result; return determinateResult; }; } function get() { return ProxyFacade; } function getCurrent() { return CurrentComponent; } safeDefineProperty(ProxyFacade, UNWRAP_PROXY, { configurable: false, writable: false, enumerable: false, value: getCurrent, }); safeDefineProperty(ProxyFacade, PROXY_KEY, { configurable: false, writable: false, enumerable: false, value: proxyKey, }); safeDefineProperty(ProxyFacade, 'toString', { configurable: true, writable: false, enumerable: false, value: function toString() { return String(CurrentComponent); }, }); function update(NextComponent) { if (typeof NextComponent !== 'function') { throw new Error('Expected a constructor.'); } if (NextComponent === CurrentComponent) { return false; } // Prevent proxy cycles const existingProxy = proxies.get(NextComponent); if (existingProxy) { return false; } isFunctionalComponent = !isReactClass(NextComponent); proxies.set(NextComponent, proxy); proxyGeneration++; // Save the next constructor so we call it const PreviousComponent = CurrentComponent; CurrentComponent = NextComponent; // Try to infer displayName const displayName = getComponentDisplayName(CurrentComponent); safeDefineProperty(ProxyFacade, 'displayName', { configurable: true, writable: false, enumerable: true, value: displayName, }); if (ProxyComponent) { safeDefineProperty(ProxyComponent, 'name', { value: displayName, }); } savedDescriptors = transferStaticProps(ProxyFacade, savedDescriptors, PreviousComponent, NextComponent); if (isFunctionalComponent || !ProxyComponent) { // nothing } else { const classHotReplacement = () => { checkLifeCycleMethods(ProxyComponent, NextComponent); if (proxyGeneration > 1) { getElementCloseHook(ProxyComponent); filteredPrototypeMethods(ProxyComponent.prototype).forEach(methodName => { if (!has.call(NextComponent.prototype, methodName)) { delete ProxyComponent.prototype[methodName]; } }); } Object.setPrototypeOf(ProxyComponent.prototype, NextComponent.prototype); defineProxyMethods(ProxyComponent, NextComponent.prototype); if (proxyGeneration > 1) { injectedMembers = mergeComponents( ProxyComponent, NextComponent, InitialComponent, lastInstance, injectedMembers, ); getElementComparisonHook(ProxyComponent); } }; // Was constructed once if (instancesCount > 0) { classHotReplacement(); } else { classUpdatePostponed = classHotReplacement; } } return true; } update(InitialComponent); const dereference = () => { proxies.delete(InitialComponent); proxies.delete(ProxyFacade); proxies.delete(CurrentComponent); }; proxy = { get, update, dereference, getCurrent: () => CurrentComponent }; proxies.set(InitialComponent, proxy); proxies.set(ProxyFacade, proxy); safeDefineProperty(proxy, UNWRAP_PROXY, { configurable: false, writable: false, enumerable: false, value: getCurrent, }); return proxy; } export default createClassProxy; ================================================ FILE: src/proxy/index.js ================================================ export * from './constants'; export { default } from './createClassProxy'; ================================================ FILE: src/proxy/inject.js ================================================ import { isNativeFunction, safeReactConstructor, getOwnKeys, shallowStringsEqual, deepPrototypeUpdate } from './utils'; import { reactLifeCycleMountMethods } from '../internal/reactUtils'; import { REGENERATE_METHOD, PREFIX, GENERATION } from './constants'; import logger from '../logger'; function mergeComponents(ProxyComponent, NextComponent, InitialComponent, lastInstance, injectedMembers) { const injectedCode = {}; try { const nextInstance = safeReactConstructor(NextComponent, lastInstance); try { // Bypass babel class inheritance checking deepPrototypeUpdate(InitialComponent, NextComponent); } catch (e) { // It was ES6 class } const proxyInstance = safeReactConstructor(ProxyComponent, lastInstance); if (!nextInstance || !proxyInstance) { return injectedCode; } const mergedAttrs = { ...proxyInstance, ...nextInstance }; const hasRegenerate = proxyInstance[REGENERATE_METHOD]; const ownKeys = getOwnKeys(Object.getPrototypeOf(ProxyComponent.prototype)); Object.keys(mergedAttrs).forEach(key => { if (key.indexOf(PREFIX) === 0) return; const nextAttr = nextInstance[key]; const prevAttr = proxyInstance[key]; if (nextAttr) { if (isNativeFunction(nextAttr) || isNativeFunction(prevAttr)) { // this is bound method const isSameArity = nextAttr.length === prevAttr.length; const existsInPrototype = ownKeys.indexOf(key) >= 0 || ProxyComponent.prototype[key]; if ((isSameArity || !prevAttr) && existsInPrototype) { if (hasRegenerate) { injectedCode[key] = `Object.getPrototypeOf(this)['${key}'].bind(this)`; } else { logger.warn( 'React Hot Loader:,', 'Non-controlled class', ProxyComponent.name, 'contains a new native or bound function ', key, nextAttr, '. Unable to reproduce', ); } } else { logger.warn( 'React Hot Loader:', 'Updated class ', ProxyComponent.name, 'contains native or bound function ', key, nextAttr, '. Unable to reproduce, use arrow functions instead.', `(arity: ${nextAttr.length}/${prevAttr.length}, proto: ${existsInPrototype ? 'yes' : 'no'}`, ); } return; } const nextString = String(nextAttr); const injectedBefore = injectedMembers[key]; const isArrow = nextString.indexOf('=>') >= 0; const isFunction = nextString.indexOf('function') >= 0 || isArrow; const referToThis = nextString.indexOf('this') >= 0; if ( nextString !== String(prevAttr) || (injectedBefore && nextString !== String(injectedBefore)) || (isArrow && referToThis) ) { if (!hasRegenerate) { if (!isFunction) { // just copy prop over injectedCode[key] = nextAttr; } else { logger.warn( 'React Hot Loader:', ' Updated class ', ProxyComponent.name, 'had different code for', key, nextAttr, '. Unable to reproduce. Regeneration support needed.', ); } } else { injectedCode[key] = nextAttr; } } else { // key was skipped } } else { // key does not exists anymore // we could not delete it, yet #840 // injectedCode[key] = null; } }); } catch (e) { logger.warn('React Hot Loader:', e); } return injectedCode; } function checkLifeCycleMethods(ProxyComponent, NextComponent) { try { const p1 = Object.getPrototypeOf(ProxyComponent.prototype); const p2 = NextComponent.prototype; reactLifeCycleMountMethods.forEach(key => { const d1 = Object.getOwnPropertyDescriptor(p1, key) || { value: p1[key] }; const d2 = Object.getOwnPropertyDescriptor(p2, key) || { value: p2[key] }; if (!shallowStringsEqual(d1, d2)) { logger.warn( 'React Hot Loader:', 'You did update', ProxyComponent.name, 's lifecycle method', key, '. Unable to repeat', ); } }); } catch (e) { // Ignore errors } } function inject(target, currentGeneration, injectedMembers) { if (target[GENERATION] !== currentGeneration) { const hasRegenerate = !!target[REGENERATE_METHOD]; Object.keys(injectedMembers).forEach(key => { try { if (hasRegenerate) { const usedThis = String(injectedMembers[key]).match(/_this([\d]+)/gi) || []; target[REGENERATE_METHOD]( key, `(function REACT_HOT_LOADER_SANDBOX () { var _this = this; // common babel transpile ${usedThis.map(name => `var ${name} = this;`)} return ${injectedMembers[key]}; }).call(this)`, ); } else { target[key] = injectedMembers[key]; } } catch (e) { logger.warn('React Hot Loader: Failed to regenerate method ', key, ' of class ', target); logger.warn('got error', e); } }); target[GENERATION] = currentGeneration; } } export { mergeComponents, checkLifeCycleMethods, inject }; ================================================ FILE: src/proxy/transferStaticProps.js ================================================ import shallowEqual from 'shallowequal'; import { safeDefineProperty } from './utils'; import { PROXY_KEY, UNWRAP_PROXY } from './constants'; const RESERVED_STATICS = [ 'length', 'displayName', 'name', 'arguments', 'caller', 'prototype', 'toString', 'valueOf', 'isStatelessFunctionalProxy', PROXY_KEY, UNWRAP_PROXY, ]; function transferStaticProps(ProxyComponent, savedDescriptors, PreviousComponent, NextComponent) { Object.getOwnPropertyNames(ProxyComponent).forEach(key => { if (RESERVED_STATICS.indexOf(key) !== -1) { return; } const prevDescriptor = Object.getOwnPropertyDescriptor(ProxyComponent, key); const savedDescriptor = savedDescriptors[key]; if (!shallowEqual(prevDescriptor, savedDescriptor)) { safeDefineProperty(NextComponent, key, prevDescriptor); } }); // Copy newly defined static methods and properties Object.getOwnPropertyNames(NextComponent).forEach(key => { if (RESERVED_STATICS.indexOf(key) !== -1) { return; } const prevDescriptor = PreviousComponent && Object.getOwnPropertyDescriptor(ProxyComponent, key); const savedDescriptor = savedDescriptors[key]; // Skip redefined descriptors if (prevDescriptor && savedDescriptor && !shallowEqual(savedDescriptor, prevDescriptor)) { safeDefineProperty(NextComponent, key, prevDescriptor); return; } if (prevDescriptor && !savedDescriptor) { safeDefineProperty(ProxyComponent, key, prevDescriptor); return; } const nextDescriptor = { ...Object.getOwnPropertyDescriptor(NextComponent, key), configurable: true, }; savedDescriptors[key] = nextDescriptor; safeDefineProperty(ProxyComponent, key, nextDescriptor); }); // Remove static methods and properties that are no longer defined Object.getOwnPropertyNames(ProxyComponent).forEach(key => { if (RESERVED_STATICS.indexOf(key) !== -1) { return; } // Skip statics that exist on the next class if (NextComponent.hasOwnProperty(key)) { return; } // Skip non-configurable statics const proxyDescriptor = Object.getOwnPropertyDescriptor(ProxyComponent, key); if (proxyDescriptor && !proxyDescriptor.configurable) { return; } const prevDescriptor = PreviousComponent && Object.getOwnPropertyDescriptor(PreviousComponent, key); const savedDescriptor = savedDescriptors[key]; // Skip redefined descriptors if (prevDescriptor && savedDescriptor && !shallowEqual(savedDescriptor, prevDescriptor)) { return; } safeDefineProperty(ProxyComponent, key, { value: undefined, }); }); return savedDescriptors; } export default transferStaticProps; ================================================ FILE: src/proxy/utils.js ================================================ /* eslint-disable no-eval, func-names */ import logger from '../logger'; export function safeReactConstructor(Component, lastInstance) { try { if (lastInstance) { return new Component(lastInstance.props, lastInstance.context); } return new Component({}, {}); } catch (e) { // some components, like Redux connect could not be created without proper context } return null; } export function isNativeFunction(fn) { return typeof fn === 'function' ? fn.toString().indexOf('[native code]') > 0 : false; } export const identity = a => a; const indirectEval = eval; export const doesSupportClasses = (function() { try { indirectEval('class Test {}'); return true; } catch (e) { return false; } })(); const ES6ProxyComponentFactory = (InitialParent, postConstructionAction) => indirectEval(` (function(InitialParent, postConstructionAction) { return class ${InitialParent.name || 'HotComponent'} extends InitialParent { /* ! THIS IS NOT YOUR COMPONENT ! ! THIS IS REACT-HOT-LOADER ! this is a "${InitialParent.name}" component, patched by React-Hot-Loader Sorry, but the real class code was hidden behind this facade Please refer to https://github.com/gaearon/react-hot-loader for details... */ constructor(props, context) { super(props, context) postConstructionAction.call(this) } } }) `)(InitialParent, postConstructionAction); const ES5ProxyComponentFactory = function(InitialParent, postConstructionAction) { function ProxyComponent(props, context) { InitialParent.call(this, props, context); postConstructionAction.call(this); } ProxyComponent.prototype = Object.create(InitialParent.prototype); Object.setPrototypeOf(ProxyComponent, InitialParent); return ProxyComponent; }; export const proxyClassCreator = doesSupportClasses ? ES6ProxyComponentFactory : ES5ProxyComponentFactory; export function getOwnKeys(target) { return [...Object.getOwnPropertyNames(target), ...Object.getOwnPropertySymbols(target)]; } export function shallowStringsEqual(a, b) { for (const key in a) { if (String(a[key]) !== String(b[key])) { return false; } } return true; } export function deepPrototypeUpdate(dest, source) { const deepDest = Object.getPrototypeOf(dest); const deepSrc = Object.getPrototypeOf(source); if (deepDest && deepSrc && deepSrc !== deepDest) { deepPrototypeUpdate(deepDest, deepSrc); } if (source.prototype && source.prototype !== dest.prototype) { dest.prototype = source.prototype; } } export function safeDefineProperty(target, key, props) { try { Object.defineProperty(target, key, props); } catch (e) { logger.warn('Error while wrapping', key, ' -> ', e); } } ================================================ FILE: src/reactHotLoader.js ================================================ /* eslint-disable no-use-before-define */ import { isCompositeComponent, getComponentDisplayName, isLazyType, isMemoType, isForwardType, isContextType, } from './internal/reactUtils'; import { increment as incrementGeneration, getHotGeneration } from './global/generation'; import { updateProxyById, resetProxies, getProxyById, isTypeBlacklisted, registerComponent, updateFunctionProxyById, addSignature, } from './reconciler/proxies'; import configuration from './configuration'; import logger from './logger'; import { preactAdapter } from './adapters/preact'; import { updateContext, updateForward, updateLazy, updateMemo } from './reconciler/fiberUpdater'; import { resolveSimpleType, resolveType } from './reconciler/resolver'; import { hotComponentCompare } from './reconciler/componentComparator'; const forceSimpleSFC = { proxy: { pureSFC: true } }; const hookWrapper = hook => { const wrappedHook = function(cb, deps) { if (configuration.reloadHooks && deps) { const inputs = [...deps]; // reload hooks which have changed string representation if (configuration.reloadHooksOnBodyChange) { inputs.push(String(cb)); } if ( // reload hooks with dependencies deps.length > 0 || // reload all hooks of option is set (configuration.reloadLifeCycleHooks && deps.length === 0) ) { inputs.push(getHotGeneration()); } return hook(cb, inputs); } return hook(cb, deps); }; wrappedHook.isPatchedByReactHotLoader = true; return wrappedHook; }; const noDeps = () => []; const reactHotLoader = { signature(type, key, getCustomHooks = noDeps) { addSignature(type, { key, getCustomHooks }); return type; }, register(type, uniqueLocalName, fileName, options = {}) { const id = `${fileName}#${uniqueLocalName}`; if ( isCompositeComponent(type) && typeof uniqueLocalName === 'string' && uniqueLocalName && typeof fileName === 'string' && fileName ) { const proxy = getProxyById(id); if (proxy && proxy.getCurrent() !== type) { if (!configuration.IS_REACT_MERGE_ENABLED) { if (isTypeBlacklisted(type) || isTypeBlacklisted(proxy.getCurrent())) { logger.error('React-hot-loader: Cold component', uniqueLocalName, 'at', fileName, 'has been updated'); } } } if (configuration.onComponentRegister) { configuration.onComponentRegister(type, uniqueLocalName, fileName); } if (configuration.onComponentCreate) { configuration.onComponentCreate(type, getComponentDisplayName(type)); } registerComponent(updateProxyById(id, type, options).get(), 2); registerComponent(type); incrementGeneration(); } if (isContextType({ type })) { // possible options - Context, Consumer, Provider. ['Provider', 'Consumer'].forEach(prop => { const descriptor = Object.getOwnPropertyDescriptor(type, prop); if (descriptor && descriptor.value) { updateFunctionProxyById(`${id}:${prop}`, descriptor.value, updateContext); } }); updateFunctionProxyById(id, type, updateContext); incrementGeneration(); } if (isLazyType({ type })) { updateFunctionProxyById(id, type, updateLazy); incrementGeneration(); } if (isForwardType({ type })) { reactHotLoader.register(type.render, `${uniqueLocalName}:render`, fileName, forceSimpleSFC); updateFunctionProxyById(id, type, updateForward); incrementGeneration(); } if (isMemoType({ type })) { reactHotLoader.register(type.type, `${uniqueLocalName}:memo`, fileName, forceSimpleSFC); updateFunctionProxyById(id, type, updateMemo); incrementGeneration(); } }, reset() { resetProxies(); }, preact(instance) { preactAdapter(instance, resolveType); }, resolveType(type) { return resolveType(type); }, patch(React, ReactDOM) { let typeResolver = resolveType; /* eslint-disable no-console */ if (ReactDOM && !ReactDOM.render) { logger.error( 'React-Hot-Loader: broken state detected, please import React-Hot-Loader before react-dom, see https://github.com/gaearon/react-hot-loader/issues/1315', ); } if (ReactDOM && ReactDOM.setHotElementComparator) { ReactDOM.setHotElementComparator(hotComponentCompare); configuration.disableHotRenderer = configuration.disableHotRendererWhenInjected; configuration.ignoreSFC = configuration.ignoreSFCWhenInjected; configuration.IS_REACT_MERGE_ENABLED = true; configuration.showReactDomPatchNotification = false; configuration.integratedComparator = true; if (ReactDOM.setHotTypeResolver) { configuration.integratedResolver = true; typeResolver = resolveSimpleType; ReactDOM.setHotTypeResolver(resolveType); } } // PATCH REACT METHODS /* eslint-enable */ if (!React.createElement.isPatchedByReactHotLoader) { const originalCreateElement = React.createElement; // Trick React into rendering a proxy so that // its state is preserved when the class changes. // This will update the proxy if it's for a known type. React.createElement = (type, ...args) => originalCreateElement(typeResolver(type), ...args); React.createElement.isPatchedByReactHotLoader = true; } if (!React.cloneElement.isPatchedByReactHotLoader) { const originalCloneElement = React.cloneElement; React.cloneElement = (element, ...args) => { const newType = element.type && typeResolver(element.type); if (newType && newType !== element.type) { return originalCloneElement( { ...element, type: newType, }, ...args, ); } return originalCloneElement(element, ...args); }; React.cloneElement.isPatchedByReactHotLoader = true; } if (!React.createFactory.isPatchedByReactHotLoader) { // Patch React.createFactory to use patched createElement // because the original implementation uses the internal, // unpatched ReactElement.createElement React.createFactory = type => { const factory = React.createElement.bind(null, type); factory.type = type; return factory; }; React.createFactory.isPatchedByReactHotLoader = true; } if (!React.Children.only.isPatchedByReactHotLoader) { const originalChildrenOnly = React.Children.only; // Use the same trick as React.createElement React.Children.only = children => originalChildrenOnly({ ...children, type: typeResolver(children.type), }); React.Children.only.isPatchedByReactHotLoader = true; } // PATCH REACT HOOKS if (React.useEffect && !React.useEffect.isPatchedByReactHotLoader) { React.useEffect = hookWrapper(React.useEffect); React.useLayoutEffect = hookWrapper(React.useLayoutEffect); React.useCallback = hookWrapper(React.useCallback); React.useMemo = hookWrapper(React.useMemo); // transform context for useContext const { useContext } = React; React.useContext = (context, ...args) => useContext(typeResolver(context), ...args); } // reactHotLoader.reset() }, }; export default reactHotLoader; ================================================ FILE: src/reconciler/componentComparator.js ================================================ import { getIdByType, getProxyByType, getSignature, isColdType, updateProxyById } from './proxies'; import { hotComparisonOpen } from '../global/generation'; import { getElementType, isContextType, isForwardType, isLazyType, isMemoType, isReactClass, isReloadableComponent, } from '../internal/reactUtils'; import { areSwappable } from './utils'; import { PROXY_KEY, UNWRAP_PROXY } from '../proxy'; import { resolveType } from './resolver'; import logger from '../logger'; import configuration from '../configuration'; import { updateLazy } from './fiberUpdater'; const getInnerComponentType = component => { const unwrapper = component[UNWRAP_PROXY]; return unwrapper ? unwrapper() : component; }; function haveEqualSignatures(prevType, nextType) { try { const prevSignature = getSignature(prevType); const nextSignature = getSignature(nextType); if (prevSignature === undefined && nextSignature === undefined) { return true; } if (prevSignature === undefined || nextSignature === undefined) { return false; } if (prevSignature.key !== nextSignature.key) { return false; } // TODO: we might need to calculate previous signature earlier in practice, // such as during the first time a component is resolved. We'll revisit this. const prevCustomHooks = prevSignature.getCustomHooks(); const nextCustomHooks = nextSignature.getCustomHooks(); if (prevCustomHooks.length !== nextCustomHooks.length) { return false; } for (let i = 0; i < nextCustomHooks.length; i++) { if (!haveEqualSignatures(prevCustomHooks[i], nextCustomHooks[i])) { return false; } } } catch (e) { logger.error('React-Hot-Loader: error occurred while comparing hook signature', e); return false; } return true; } const areSignaturesCompatible = (a, b) => { // compare signatures of two components // non-equal component have to remount and there is two options to do it // - fail the comparison, remounting all tree below // - fulfill it, but set `_debugNeedsRemount` on a fiber to drop only local state // the second way is not published yet, so going with the first one if (!haveEqualSignatures(a, b)) { logger.warn('⚛️🔥🎣 Hook order change detected: component', a, 'has been remounted'); return false; } return true; }; const compareRegistered = (a, b) => getIdByType(a) === getIdByType(b) && getProxyByType(a) === getProxyByType(b) && areSignaturesCompatible(a, b); const areDeepSwappable = (oldType, newType) => { const type = { type: oldType }; if (typeof oldType === 'function') { return areSwappable(oldType, newType); } if (isForwardType(type)) { return areDeepSwappable(oldType.render, newType.render); } if (isMemoType(type)) { return areDeepSwappable(oldType.type, newType.type); } // that's not safe // if (isLazyType(type)) { // return areDeepSwappable(oldType._ctor, newType._ctor) // } return false; }; const compareComponents = (oldType, newType, setNewType, baseType) => { let defaultResult = oldType === newType; if ( (oldType && !newType) || (!oldType && newType) || typeof oldType !== typeof newType || getElementType(oldType) !== getElementType(newType) || 0 ) { return defaultResult; } if (getIdByType(newType) || getIdByType(oldType)) { if (!compareRegistered(oldType, newType)) { return false; } defaultResult = true; } if (isForwardType({ type: oldType }) && isForwardType({ type: newType })) { if (!compareRegistered(oldType.render, newType.render)) { return false; } if (oldType.render === newType.render || areDeepSwappable(oldType, newType)) { setNewType(newType); return true; } return defaultResult; } if (isMemoType({ type: oldType }) && isMemoType({ type: newType })) { if (!compareRegistered(oldType.type, newType.type)) { return false; } if (oldType.type === newType.type || areDeepSwappable(oldType, newType)) { if (baseType) { // memo form different fibers, why? if (baseType.$$typeof === newType.$$typeof) { setNewType(newType); } else { setNewType(newType.type); } } else { logger.warn('Please update hot-loader/react-dom'); if (isReactClass(newType.type)) { setNewType(newType); } else { setNewType(newType.type); } } return true; } return defaultResult; } if (isLazyType({ type: oldType })) { updateLazy(oldType, newType); // no need to update // setNewType(newType); return defaultResult; } if (isContextType({ type: oldType })) { // update provider setNewType(newType); return defaultResult; } if ( typeof newType === 'function' && (defaultResult || (newType !== oldType && areSignaturesCompatible(newType, oldType) && areSwappable(newType, oldType))) ) { const unwrapFactory = newType[UNWRAP_PROXY]; const oldProxy = unwrapFactory && getProxyByType(unwrapFactory()); if (oldProxy) { oldProxy.dereference(); updateProxyById(oldType[PROXY_KEY] || getIdByType(oldType), getInnerComponentType(newType)); } else { setNewType(newType); } return true; } return defaultResult; }; const knownPairs = new WeakMap(); const emptyMap = new WeakMap(); const getKnownPair = (oldType, newType) => { const pair = knownPairs.get(oldType) || emptyMap; return pair.get(newType); }; export const hotComponentCompare = (oldType, preNewType, setNewType, baseType) => { const hotActive = hotComparisonOpen(); const newType = configuration.integratedResolver ? resolveType(preNewType) : preNewType; // TODO: find out the root cause // we could not use "fast result" here - go a full part to update a fiber. // const knownType = getKnownPair(oldType, newType); // if (knownType !== undefined) { // return knownType; // } let result = oldType === newType; if (hotActive) { // pre fail components which could not be merged if ( !isReloadableComponent(oldType) || !isReloadableComponent(newType) || isColdType(oldType) || isColdType(oldType) || !oldType || !newType || 0 ) { return result; } result = compareComponents(oldType, newType, setNewType, baseType); const pair = knownPairs.get(oldType) || new WeakMap(); pair.set(newType, result); knownPairs.set(oldType, pair); return result; } // result - true if components are equal, or were "equal" at any point in the past return result || getKnownPair(oldType, newType) || false; }; ================================================ FILE: src/reconciler/fiberUpdater.js ================================================ /* eslint-disable no-underscore-dangle */ import React from 'react'; import configuration from '../configuration'; import { enterHotUpdate } from '../global/generation'; import AppContainer from '../AppContainer.dev'; import { resolveType } from './resolver'; const lazyConstructor = '_ctor'; const getLazyConstructor = target => { // React 16 if (target[lazyConstructor]) { return target[lazyConstructor]; } // React 17 if (target._payload) { return target._payload._result; } return null; }; const setLazyConstructor = (target, replacement) => { replacement.isPatchedByReactHotLoader = true; // React 16 if (target[lazyConstructor]) { target[lazyConstructor] = replacement; } // React 17 else if (target._payload) { target._payload._hotUpdated = true; target._payload._result = replacement; } else { console.error('could not update lazy component'); } }; const patched = fn => { fn.isPatchedByReactHotLoader = true; return fn; }; const patchLazyConstructor = target => { if (configuration.wrapLazy && !getLazyConstructor(target).isPatchedByReactHotLoader) { const ctor = getLazyConstructor(target); setLazyConstructor(target, () => ctor().then(m => { const C = resolveType(m.default); // chunks has been updated - new hot loader process is taking a place enterHotUpdate(); if (!React.forwardRef) { return { default: patched(props => ( )), }; } return { default: patched( // eslint-disable-next-line prefer-arrow-callback React.forwardRef(function HotLoaderLazyWrapper(props, ref) { return ( ); }), ), }; }), ); } }; export const updateLazy = (target, type) => { const ctor = getLazyConstructor(type); if (getLazyConstructor(target) !== ctor) { // just execute `import` and RHL.register will do the job ctor(); } patchLazyConstructor(target); patchLazyConstructor(type); }; export const updateMemo = (target, { type }) => { target.type = resolveType(type); }; export const updateForward = (target, { render }) => { target.render = render; }; export const updateContext = () => { // nil }; ================================================ FILE: src/reconciler/hotReplacementRender.js ================================================ import { PROXY_IS_MOUNTED, PROXY_KEY, UNWRAP_PROXY } from '../proxy'; import { getIdByType, isRegisteredComponent, isTypeBlacklisted, updateProxyById } from './proxies'; import { updateInstance, getComponentDisplayName, isFragmentNode, isContextConsumer, isContextProvider, getContextProvider, isReactClassInstance, CONTEXT_CURRENT_VALUE, isMemoType, isLazyType, isForwardType, } from '../internal/reactUtils'; import logger from '../logger'; import configuration, { internalConfiguration } from '../configuration'; import { areSwappable } from './utils'; import { resolveType } from './resolver'; import { hotRenderWithHooks } from '../internal/stack/hydrateFiberStack'; let renderStack = []; const stackReport = () => { const rev = renderStack.slice().reverse(); logger.warn('in', rev[0].name, rev); }; const emptyMap = new Map(); const stackContext = () => (renderStack[renderStack.length - 1] || {}).context || emptyMap; const shouldUseRenderMethod = fn => fn && (isReactClassInstance(fn) || fn.SFC_fake); const getElementType = child => (child.type[UNWRAP_PROXY] ? child.type[UNWRAP_PROXY]() : child.type); const filterNullArray = a => { if (!a) return []; return a.filter(x => !!x); }; const unflatten = a => a.reduce((acc, a) => { if (Array.isArray(a)) { acc.push(...unflatten(a)); } else { acc.push(a); } return acc; }, []); const isArray = fn => Array.isArray(fn); const asArray = a => (isArray(a) ? a : [a]); const render = (component, stack) => { if (!component) { return []; } if (component.hotComponentUpdate) { component.hotComponentUpdate(); } if (shouldUseRenderMethod(component)) { // not calling real render method to prevent call recursion. // stateless components does not have hotComponentRender return component.hotComponentRender ? component.hotComponentRender() : component.render(); } if (isForwardType(component)) { // render forward type in a sandbox return hotRenderWithHooks(stack.fiber, () => component.type.render(component.props, null)); } if (isArray(component)) { return component.map(render); } if (component.children) { return component.children; } return []; }; const NO_CHILDREN = { children: [] }; const mapChildren = (children, instances) => ({ children: children.filter(c => c).map((child, index) => { if (typeof child !== 'object' || child.isMerged) { return child; } const instanceLine = instances[index] || {}; const oldChildren = asArray(instanceLine.children || []); if (Array.isArray(child)) { return { type: null, ...mapChildren(child, oldChildren), }; } const newChildren = asArray((child.props && child.props.children) || child.children || []); const nextChildren = child.type !== 'function' && oldChildren.length && mapChildren(newChildren, oldChildren); return { nextProps: child.props, isMerged: true, ...instanceLine, // actually child merge is needed only for "HTML TAG"s, and usually don't work for Components. // the children from an instance or rendered children // while children from a props is just a props. // they could not exists. they could differ. ...(nextChildren || {}), type: child.type, }; }), }); const mergeInject = (a, b, instance) => { if (a && !Array.isArray(a)) { return mergeInject([a], b); } if (b && !Array.isArray(b)) { return mergeInject(a, [b]); } if (!a || !b) { return NO_CHILDREN; } if (a.length === b.length) { return mapChildren(a, b); } // in some cases (no confidence here) B could contain A except null children // in some cases - could not. // this depends on React version and the way you build component. const nonNullA = filterNullArray(a); if (nonNullA.length === b.length) { return mapChildren(nonNullA, b); } const flatA = unflatten(nonNullA); const flatB = unflatten(b); if (flatA.length === flatB.length) { return mapChildren(flatA, flatB); } if (flatB.length === 0 && flatA.length === 1 && typeof flatA[0] !== 'object') { // terminal node } else if (!configuration.IS_REACT_MERGE_ENABLED) { logger.warn(`React-hot-loader: unable to merge `, a, 'and children of ', instance); stackReport(); } return NO_CHILDREN; }; const transformFlowNode = flow => flow.reduce((acc, node) => { if (node && isFragmentNode(node)) { if (node.props && node.props.children) { return [...acc, ...filterNullArray(asArray(node.props.children))]; } if (node.children) { return [...acc, ...filterNullArray(asArray(node.children))]; } } return [...acc, node]; }, []); let scheduledUpdates = []; let scheduledUpdate = 0; export const flushScheduledUpdates = () => { const instances = scheduledUpdates; scheduledUpdates = []; scheduledUpdate = 0; instances.forEach(instance => instance[PROXY_IS_MOUNTED] && updateInstance(instance)); }; export const unscheduleUpdate = instance => { scheduledUpdates = scheduledUpdates.filter(inst => inst !== instance); }; const scheduleInstanceUpdate = instance => { scheduledUpdates.push(instance); if (!scheduledUpdate) { scheduledUpdate = setTimeout(flushScheduledUpdates, 4); } }; const hotReplacementRender = (instance, stack) => { if (isReactClassInstance(instance)) { const type = getElementType(stack); renderStack.push({ name: getComponentDisplayName(type), type, props: stack.instance.props, context: stackContext(), }); } try { const flow = transformFlowNode(filterNullArray(asArray(render(instance, stack)))); const { children } = stack; flow.forEach((child, index) => { let childType = child.type; const stackChild = children[index]; const next = instance => { // copy over props as long new component may be hidden inside them // child does not have all props, as long some of them can be calculated on componentMount. const realProps = instance.props; const nextProps = { ...realProps, ...(child.nextProps || {}), ...(child.props || {}), }; if (isReactClassInstance(instance) && instance.componentWillUpdate) { // Force-refresh component (bypass redux renderedComponent) instance.componentWillUpdate({ ...realProps }, instance.state); } instance.props = nextProps; hotReplacementRender(instance, stackChild); instance.props = realProps; }; // text node if (typeof child !== 'object' || !stackChild || !stackChild.instance) { if (stackChild && stackChild.children && stackChild.children.length) { logger.error( 'React-hot-loader: reconciliation failed', 'could not dive into [', child, '] while some elements are still present in the tree.', ); stackReport(); } return; } // comparing rendered type to fiber.ElementType if (typeof childType !== typeof stackChild.elementType) { // Portals could generate undefined !== null if (childType && stackChild.type) { logger.warn('React-hot-loader: got ', childType, 'instead of', stackChild.type); stackReport(); } return; } if (isMemoType(child) || isLazyType(child)) { // force update memo children if (stackChild.children && stackChild.children[0]) { scheduleInstanceUpdate(stackChild.children[0].instance); } childType = childType.type || childType; } if (isForwardType(child)) { next(stackChild.instance); } else if (isContextConsumer(child)) { try { const contextValue = stackContext().get(getContextProvider(childType)); next({ children: (child.props ? child.props.children : child.children[0])( contextValue !== undefined ? contextValue : childType[CONTEXT_CURRENT_VALUE], ), }); } catch (e) { // do nothing, yet } } else if (typeof childType !== 'function') { // React let childName = childType ? getComponentDisplayName(childType) : 'empty'; let extraContext = stackContext(); if (isContextProvider(child)) { extraContext = new Map(extraContext); extraContext.set( getContextProvider(childType), { ...(child.nextProps || {}), ...(child.props || {}), }.value, ); childName = 'ContextProvider'; } renderStack.push({ name: childName, type: childType, props: stack.instance.props, context: extraContext, }); next( // move types from render to the instances of hydrated tree mergeInject( transformFlowNode(asArray(child.props ? child.props.children : child.children)), stackChild.instance.children, stackChild.instance, ), ); renderStack.pop(); } else { if (childType === stackChild.type) { next(stackChild.instance); } else { // unwrap proxy let childType = getElementType(child); if (isMemoType(child)) { childType = childType.type || childType; } if (!stackChild.type[PROXY_KEY]) { if (!configuration.IS_REACT_MERGE_ENABLED) { if (isTypeBlacklisted(stackChild.type)) { logger.warn('React-hot-loader: cold element got updated ', stackChild.type); } } } if (isRegisteredComponent(childType) || isRegisteredComponent(stackChild.type)) { // one of elements are registered via babel plugin, and should not be handled by hot swap if (resolveType(childType) === resolveType(stackChild.type)) { next(stackChild.instance); } else { // one component replace another. This is normal situation } } else if (areSwappable(childType, stackChild.type)) { // they are both registered, or have equal code/displayname/signature // update proxy using internal PROXY_KEY updateProxyById(stackChild.type[PROXY_KEY] || getIdByType(stackChild.type), childType); next(stackChild.instance); } else { logger.warn( `React-hot-loader: a ${getComponentDisplayName(childType)} was found where a ${getComponentDisplayName( stackChild, )} was expected. ${childType}`, ); stackReport(); } } scheduleInstanceUpdate(stackChild.instance); } }); } catch (e) { if (e.then) { // this is probably Suspense. Do nothing } else { logger.warn('React-hot-loader: run time error during reconciliation', e); } } if (isReactClassInstance(instance)) { renderStack.pop(); } }; export default (instance, stack) => { if (configuration.disableHotRenderer) { return; } try { // disable reconciler to prevent upcoming components from proxying. internalConfiguration.disableProxyCreation = true; renderStack = []; hotReplacementRender(instance, stack); } catch (e) { logger.warn('React-hot-loader: reconcilation failed due to error', e); } finally { internalConfiguration.disableProxyCreation = false; } }; ================================================ FILE: src/reconciler/index.js ================================================ import getReactStack, { deepMarkUpdate, cleanupReact } from '../internal/getReactStack'; import hotReplacementRender, { flushScheduledUpdates, unscheduleUpdate } from './hotReplacementRender'; const reconcileHotReplacement = ReactInstance => { const stack = getReactStack(ReactInstance); hotReplacementRender(ReactInstance, stack); cleanupReact(); deepMarkUpdate(stack); }; export { flushScheduledUpdates, unscheduleUpdate }; export default reconcileHotReplacement; ================================================ FILE: src/reconciler/proxies.js ================================================ import createProxy, { PROXY_KEY } from '../proxy'; import { resetClassProxies } from '../proxy/createClassProxy'; import { isCompositeComponent, isReactClass } from '../internal/reactUtils'; import configuration from '../configuration'; import { incrementHotGeneration } from '../global/generation'; import { merge } from './utils'; let signatures; let proxiesByID; let blackListedProxies; let registeredComponents; let idsByType; let elementCount = 0; let renderOptions = {}; let componentOptions; const generateTypeId = () => `auto-${elementCount++}`; export const getIdByType = type => idsByType.get(type); export const isProxyType = type => type[PROXY_KEY]; export const getProxyById = id => proxiesByID[id]; export const getProxyByType = type => getProxyById(getIdByType(type)); export const registerComponent = type => registeredComponents.set(type, 1); export const isRegisteredComponent = type => registeredComponents.has(type); export const setStandInOptions = options => { renderOptions = options; }; export const updateFunctionProxyById = (id, type, updater) => { // Remember the ID. idsByType.set(type, id); const proxy = proxiesByID[id]; if (!proxy) { proxiesByID[id] = type; } updater(proxiesByID[id], type); // proxiesByID[id] = type; // keep the first ref return proxiesByID[id]; }; export const updateProxyById = (id, type, options = {}) => { if (!id) { return null; } // Remember the ID. idsByType.set(type, id); if (!proxiesByID[id]) { proxiesByID[id] = createProxy( type, id, merge({}, renderOptions, { proxy: componentOptions.get(type) || {} }, options), ); } else if (proxiesByID[id].update(type)) { // proxy could be registered again only in case of HMR incrementHotGeneration(); } return proxiesByID[id]; }; export const createProxyForType = (type, options) => getProxyByType(type) || updateProxyById(generateTypeId(), type, options); export const isColdType = type => blackListedProxies.has(type); export const isTypeBlacklisted = type => isColdType(type) || (isCompositeComponent(type) && ((configuration.ignoreSFC && !isReactClass(type)) || (configuration.ignoreComponents && isReactClass(type)))); export const blacklistByType = type => blackListedProxies.set(type, true); export const setComponentOptions = (component, options) => componentOptions.set(component, options); export const addSignature = (type, signature) => signatures.set(type, signature); export const getSignature = type => signatures.get(type); export const resetProxies = () => { proxiesByID = {}; idsByType = new WeakMap(); blackListedProxies = new WeakMap(); registeredComponents = new WeakMap(); componentOptions = new WeakMap(); signatures = new WeakMap(); resetClassProxies(); }; resetProxies(); ================================================ FILE: src/reconciler/proxyAdapter.js ================================================ import React from 'react'; import { enterHotUpdate, get as getGeneration, hotComparisonOpen, setComparisonHooks } from '../global/generation'; import { getProxyByType, setStandInOptions } from './proxies'; import reconcileHotReplacement, { flushScheduledUpdates, unscheduleUpdate } from './index'; import configuration, { internalConfiguration } from '../configuration'; import { EmptyErrorPlaceholder, logException } from '../errorReporter'; import { RENDERED_GENERATION } from '../proxy'; export const renderReconciler = (target, force) => { // we are not inside parent reconcilation const currentGeneration = getGeneration(); const componentGeneration = target[RENDERED_GENERATION]; target[RENDERED_GENERATION] = currentGeneration; if (!internalConfiguration.disableProxyCreation) { if ((componentGeneration || force) && componentGeneration !== currentGeneration) { enterHotUpdate(); reconcileHotReplacement(target); return true; } } return false; }; function asyncReconciledRender(target) { renderReconciler(target, false); } export function proxyWrapper(element) { // post wrap on post render if (!internalConfiguration.disableProxyCreation) { unscheduleUpdate(this); } if (!element) { return element; } if (Array.isArray(element)) { return element.map(proxyWrapper); } if (typeof element.type === 'function') { const proxy = getProxyByType(element.type); if (proxy) { return { ...element, type: proxy.get(), }; } } return element; } const ERROR_STATE = 'react_hot_loader_catched_error'; const ERROR_STATE_PROTO = 'react_hot_loader_catched_error-prototype'; const OLD_RENDER = 'react_hot_loader_original_render'; function componentDidCatch(error, errorInfo) { this[ERROR_STATE] = { location: 'boundary', error, errorInfo, generation: getGeneration(), }; Object.getPrototypeOf(this)[ERROR_STATE_PROTO] = this[ERROR_STATE]; if (!configuration.errorReporter) { logException(error, errorInfo, this); } this.forceUpdate(); } function componentRender(...args) { const { error, errorInfo, generation } = this[ERROR_STATE] || {}; if (error && generation === getGeneration()) { return React.createElement(configuration.errorReporter || EmptyErrorPlaceholder, { error, errorInfo, component: this, }); } if (this.hotComponentUpdate) { this.hotComponentUpdate(); } try { return this[OLD_RENDER].render.call(this, ...args); } catch (renderError) { this[ERROR_STATE] = { location: 'render', error: renderError, generation: getGeneration(), }; if (!configuration.errorReporter) { logException(renderError, undefined, this); } return componentRender.call(this); } } export function retryHotLoaderError() { delete this[ERROR_STATE]; this.forceUpdate(); } setComparisonHooks( () => ({}), component => { if (!hotComparisonOpen()) { return; } const { prototype } = component; if (!prototype[OLD_RENDER]) { const renderDescriptior = Object.getOwnPropertyDescriptor(prototype, 'render'); prototype[OLD_RENDER] = { descriptor: renderDescriptior ? renderDescriptior.value : undefined, render: prototype.render, }; prototype.componentDidCatch = componentDidCatch; prototype.retryHotLoaderError = retryHotLoaderError; prototype.render = componentRender; } delete prototype[ERROR_STATE]; }, ({ prototype }) => { if (prototype[OLD_RENDER]) { const { generation } = prototype[ERROR_STATE_PROTO] || {}; if (generation === getGeneration()) { // still in error. // keep render hooked } else { delete prototype.componentDidCatch; delete prototype.retryHotLoaderError; // undo only what we did if (prototype.render === componentRender) { if (!prototype[OLD_RENDER].descriptor) { delete prototype.render; } else { prototype.render = prototype[OLD_RENDER].descriptor; } } else { console.error('React-Hot-Loader: something unexpectedly mutated Component', prototype); } delete prototype[ERROR_STATE_PROTO]; delete prototype[OLD_RENDER]; } } }, ); setStandInOptions({ componentWillRender: asyncReconciledRender, componentDidRender: proxyWrapper, componentDidUpdate: component => { component[RENDERED_GENERATION] = getGeneration(); flushScheduledUpdates(); }, }); ================================================ FILE: src/reconciler/resolver.js ================================================ import { createProxyForType, getProxyByType, isProxyType, isTypeBlacklisted } from './proxies'; import { getComponentDisplayName, isCompositeComponent, isContextType, isForwardType, isLazyType, isMemoType, } from '../internal/reactUtils'; import configuration, { internalConfiguration } from '../configuration'; const shouldNotPatchComponent = type => isTypeBlacklisted(type); export function resolveUtility(type) { // all "utility" types are resolved to their __initial__ shapes // that enables to never change reference to them, and gives the ability to maintain React Tree on HMR // all operations could be skipped with react-hot-dom enabled if (typeof type === 'object') { if (configuration.integratedComparator) { return type; } const element = { type }; if (isLazyType(element) || isMemoType(element) || isForwardType(element) || isContextType(element)) { return getProxyByType(type) || type; } } return undefined; } export function resolveComponent(type, options = {}) { const existingProxy = getProxyByType(type); // cold API if (shouldNotPatchComponent(type)) { return existingProxy ? existingProxy.getCurrent() : type; } if (!existingProxy && configuration.onComponentCreate) { configuration.onComponentCreate(type, getComponentDisplayName(type)); if (shouldNotPatchComponent(type)) { return type; } } const proxy = internalConfiguration.disableProxyCreation ? existingProxy : createProxyForType(type, options); return proxy ? proxy.get() : undefined; } export function resolveProxy(type) { if (isProxyType(type)) { return type; } return undefined; } export function resolveNotComponent(type) { if (!isCompositeComponent(type)) { return type; } return undefined; } export const getLatestTypeVersion = type => { const existingProxy = getProxyByType(type); return existingProxy && existingProxy.getCurrent && existingProxy.getCurrent(); }; export const resolveSimpleType = type => { if (!type) { return type; } const simpleResult = resolveProxy(type) || resolveUtility(type) || resolveNotComponent(type); if (simpleResult) { return simpleResult; } const lastType = getLatestTypeVersion(type); // only lazy loaded components any now failing into this branch // if (lastType && lastType !== type) { // console.warn('RHL: used type', type, 'is obsolete. Something is wrong with HMR.'); // } return lastType || type; }; export const resolveType = (type, options = {}) => { if (!type) { return type; } return ( resolveProxy(type) || resolveUtility(type) || resolveNotComponent(type) || resolveComponent(type, options) || type ); }; ================================================ FILE: src/reconciler/utils.js ================================================ import levenshtein from 'fast-levenshtein'; import { getIdByType } from './proxies'; import { getComponentDisplayName, isReactClass } from '../internal/reactUtils'; // some `empty` names, React can autoset display name to... const UNDEFINED_NAMES = { Unknown: true, Component: true, }; const areNamesEqual = (a, b) => a === b || (UNDEFINED_NAMES[a] && UNDEFINED_NAMES[b]); const isFunctional = fn => typeof fn === 'function'; const getTypeOf = type => { if (isReactClass(type)) return 'ReactComponent'; if (isFunctional(type)) return 'StatelessFunctional'; return 'Fragment'; // ? }; function clearStringFast(str) { return str.length < 12 ? str : ` ${str}`.slice(1); } const haveTextSimilarity = (a, b) => // equal or slight changed a === b || levenshtein.get(clearStringFast(a), clearStringFast(b)) < a.length * 0.2; const getBaseProto = source => source.prototype.hotComponentRender ? Object.getPrototypeOf(source.prototype) : source.prototype; const equalClasses = (a, b) => { const prototypeA = getBaseProto(a); const prototypeB = getBaseProto(b); let hits = 0; let misses = 0; let comparisons = 0; Object.getOwnPropertyNames(prototypeA).forEach(key => { const descriptorA = Object.getOwnPropertyDescriptor(prototypeA, key); const valueA = descriptorA && (descriptorA.value || descriptorA.get || descriptorA.set); const descriptorB = Object.getOwnPropertyDescriptor(prototypeB, key); const valueB = descriptorB && (descriptorB.value || descriptorB.get || descriptorB.set); if (typeof valueA === 'function' && key !== 'constructor') { comparisons++; if (haveTextSimilarity(String(valueA), String(valueB))) { hits++; } else { misses++; if (key === 'render') { misses++; } } } }); // allow to add or remove one function return (hits > 0 && misses <= 1) || comparisons === 0; }; export const areSwappable = (a, b) => { // both are registered components and have the same name if (getIdByType(b) && getIdByType(a) === getIdByType(b)) { return true; } if (getTypeOf(a) !== getTypeOf(b)) { return false; } if (isReactClass(a)) { return areNamesEqual(getComponentDisplayName(a), getComponentDisplayName(b)) && equalClasses(a, b); } if (isFunctional(a)) { const nameA = getComponentDisplayName(a); if (!areNamesEqual(nameA, getComponentDisplayName(b))) { return false; } return nameA !== 'Component' || haveTextSimilarity(String(a), String(b)); } return false; }; export function merge(...sources) { let acc = {}; for (const source of sources) { if (source instanceof Array) { if (!(acc instanceof Array)) { acc = []; } acc = [...acc, ...source]; } else if (source instanceof Object) { for (const key of Object.keys(source)) { let value = source[key]; if (value instanceof Object && key in acc) { value = merge(acc[key], value); } acc = { ...acc, [key]: value }; } } } return acc; } ================================================ FILE: src/utils/runQueue.js ================================================ export const createQueue = (runner = a => a()) => { let promise; let queue = []; const runAll = () => { const oldQueue = queue; oldQueue.forEach(cb => cb()); queue = []; }; const add = cb => { if (queue.length === 0) { promise = Promise.resolve().then(() => runner(runAll)); } queue.push(cb); return promise; }; return add; }; ================================================ FILE: src/utils.dev.js ================================================ import { blacklistByType, getProxyByType, setComponentOptions } from './reconciler/proxies'; import { setConfiguration } from './configuration'; import { hotComponentCompare } from './reconciler/componentComparator'; const getProxyOrType = type => { const proxy = getProxyByType(type); return proxy ? proxy.get() : type; }; export const areComponentsEqual = (a, b) => getProxyOrType(a) === getProxyOrType(b); export const compareOrSwap = (oldType, newType) => hotComponentCompare(oldType, newType); export const cold = type => { blacklistByType(type); return type; }; export const configureComponent = (component, options) => setComponentOptions(component, options); export const setConfig = config => setConfiguration(config); ================================================ FILE: src/utils.prod.js ================================================ export const areComponentsEqual = (a, b) => a === b; export const setConfig = () => {}; export const cold = type => type; export const configureComponent = () => {}; ================================================ FILE: src/webpack/index.js ================================================ 'use strict'; const fs = require('fs'); const path = require('path'); const {SourceNode, SourceMapConsumer} = require('source-map'); const loaderUtils = require('loader-utils'); const makeIdentitySourceMap = require('./makeIdentitySourceMap'); const patch = require('./patch'); let tagCommonJSExportsSource = null; function transform(source, map) { const callback = this.async(); const resourcePath = this.resourcePath; if (process.env.NODE_ENV === 'production') { return callback(null, source, map); } if (source && source.types && source.types.IfStatement) { throw new Error( 'React Hot Loader: You are erroneously trying to use a Webpack loader ' + 'as a Babel plugin. Replace "react-hot-loader/webpack" with ' + '"react-hot-loader/babel" in the "plugins" section of your .babelrc file. ' + 'While we recommend the above, if you prefer not to use Babel, ' + 'you may remove "react-hot-loader/webpack" from the "plugins" section of ' + 'your .babelrc file altogether, and instead add "react-hot-loader/webpack" ' + 'to the "loaders" section of your Webpack configuration.', ); } if (this.cacheable) { this.cacheable(); } const options = Object.assign({withPatch: true}, loaderUtils.getOptions(this)); if (options.withPatch) { source = patch(source); } if (source.indexOf('reactHotLoader.register') > 0 || options.noRegister) { return callback(null, source, map); } // This is a Webpack loader, but the user put it in the Babel config. // Read the helper once. if (!tagCommonJSExportsSource) { tagCommonJSExportsSource = fs .readFileSync(path.join(__dirname, 'webpackTagCommonJSExports.js'), 'utf8') // Babel inserts these. // Ideally we'd opt out for one file but this is simpler. .replace(/['"]use strict['"];/, '') // eslint comments don't need to end up in the output .replace(/\/\* (.*) \*\//, '') .replace(/\/\/ eslint-disable-line .*\n/g, '\n') .replace(/\/\* global.*\*\//, '') .split(/\n\s*/) .join(' '); } // Parameterize the helper with the current filename. const separator = '\n'; const appendText = tagCommonJSExportsSource.replace(/__FILENAME__/g, JSON.stringify(resourcePath)); if (this.sourceMap === false) { return callback(null, [source, appendText].join(separator)); } if (!map) { map = makeIdentitySourceMap(source, resourcePath); // eslint-disable-line no-param-reassign } const sourceMapConsumer = new SourceMapConsumer(map); const onSourceMapReady = consumedMap => { const node = new SourceNode(null, null, null, [ SourceNode.fromStringWithSourceMap(source, consumedMap), new SourceNode(null, null, resourcePath, appendText), ]).join(separator); const result = node.toStringWithSourceMap(); callback(null, result.code, result.map.toJSON() || undefined); }; if (sourceMapConsumer.then) { sourceMapConsumer.then(onSourceMapReady); } else { onSourceMapReady(sourceMapConsumer); } } transform.patch = patch; module.exports = transform; ================================================ FILE: src/webpack/makeIdentitySourceMap.js ================================================ const { SourceMapGenerator } = require('source-map'); function makeIdentitySourceMap(content, resourcePath) { const map = new SourceMapGenerator(); map.setSourceContent(resourcePath, content); content.split('\n').forEach((line, index) => { map.addMapping({ source: resourcePath, original: { line: index + 1, column: 0, }, generated: { line: index + 1, column: 0, }, }); }); return map.toJSON(); } module.exports = makeIdentitySourceMap; ================================================ FILE: src/webpack/patch.js ================================================ const injectionStart = { '16.13': [ 'isCompatibleFamilyForHotReloading(child, element)', 'hotCompareElements(child.elementType, element.type, hotUpdateChild(child), child.type)' ], '16.10': [ 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type || ( // Keep this check inline so it only runs on the false path:\n isCompatibleFamilyForHotReloading(child, element)))', 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : hotCompareElements(child.elementType, element.type, hotUpdateChild(child), child.type))' ], '16.9': [ 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type || (\n // Keep this check inline so it only runs on the false path:\n isCompatibleFamilyForHotReloading(child, element)))', 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : hotCompareElements(child.elementType, element.type, hotUpdateChild(child), child.type))' ], '16.6': [ 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type)', 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : hotCompareElements(child.elementType, element.type, hotUpdateChild(child), child.type))', ], '16.6-compact': [ 'if(child.tag===Fragment?element.type===REACT_FRAGMENT_TYPE:child.elementType===element.type)', 'if(child.tag===Fragment?element.type===REACT_FRAGMENT_TYPE:hotCompareElements(child.elementType,element.type, hotUpdateChild(child), child.type))', ], '16.4': [ 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.type === element.type) {', 'if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : hotCompareElements(child.type, element.type, hotUpdateChild(child), child.type)) {', ], '16.4-compact': [ 'if(child.tag===Fragment?element.type===REACT_FRAGMENT_TYPE:child.type===element.type)', 'if(child.tag===Fragment?element.type===REACT_FRAGMENT_TYPE:hotCompareElements(child.type,element.type, hotUpdateChild(child), child.type))', ], }; const additional = { '16.13-update': [ 'isCompatibleFamilyForHotReloading(current, element)', 'hotCompareElements(current.elementType, element.type, hotUpdateChild(current), current.type)' ], '16.10-update': [ 'current$$1.elementType === element.type || ( // Keep this check inline so it only runs on the false path:\n isCompatibleFamilyForHotReloading(current$$1, element)))', '(hotCompareElements(current$$1.elementType, element.type, hotUpdateChild(current$$1), current$$1.type)))' ], '16.9-update': [ 'current$$1.elementType === element.type || (\n // Keep this check inline so it only runs on the false path:\n isCompatibleFamilyForHotReloading(current$$1, element)))', '(hotCompareElements(current$$1.elementType, element.type, hotUpdateChild(current$$1), current$$1.type)))' ], '16.6-update': [ 'if (current$$1 !== null && current$$1.elementType === element.type) {', 'if (current$$1 !== null && hotCompareElements(current$$1.elementType, element.type, hotUpdateChild(current$$1),current$$1.type)) {', ], '16.6-update-compact': [ 'if(current$$1!==null&¤t$$1.elementType===element.type)', 'if(current$$1!==null&&hotCompareElements(current$$1.elementType,element.type,hotUpdateChild(current$$1),current$$1.type))', ], '16.4-update': [ 'if (current !== null && current.type === element.type) {', 'if (current !== null && hotCompareElements(current.type, element.type, hotUpdateChild(current),current.type)) {', ], '16.4-update-compact': [ 'if (current!== null&¤t.type===element.type)', 'if (current!== null&&hotCompareElements(current.type,element.type,hotUpdateChild(current)))', ], '17.0.0-rc1': [ 'function createFiberFromTypeAndProps(type, // React$ElementType\n' + 'key, pendingProps, owner, mode, lanes) {', 'function createFiberFromTypeAndProps(type, // React$ElementType\n' + 'key, pendingProps, owner, mode, lanes) {type = hotResolveType(type);', ], '17.0.0-rc1-compact': [ 'function createFiberFromTypeAndProps(type,// React$ElementType\n' + 'key,pendingProps,owner,mode,lanes){', 'function createFiberFromTypeAndProps(type,// React$ElementType\n' + 'key,pendingProps,owner,mode,lanes){type = hotResolveType(type);', ], '16.8-type': [ 'function createFiberFromTypeAndProps(type, // React$ElementType\nkey, pendingProps, owner, mode, expirationTime) {', 'function createFiberFromTypeAndProps(type, // React$ElementType\nkey, pendingProps, owner, mode, expirationTime) {type = hotResolveType(type);', ], '16.8-type-compact': [ 'function createFiberFromTypeAndProps(type,// React$ElementType\nkey,pendingProps,owner,mode,expirationTime){', 'function createFiberFromTypeAndProps(type,// React$ElementType\nkey,pendingProps,owner,mode,expirationTime){type = hotResolveType(type);', ] }; const reactHotLoaderCode = ` var hotUpdateChild = function (child) { return function (newType) { child.type = newType; if (child.alternate) { child.alternate.type = newType; } } }; var hotResolveType = function (type) { return type; }; var hotCompareElements = function (oldType, newType) { return oldType === newType }; var hotCleanupHooks = function () { if (typeof resetHooks !== 'undefined') { resetHooks(); } } var evalInReactContext = function (injection) { return eval(injection); }; var hotCleanup = hotCleanupHooks; var hotRenderWithHooks = function (current, render) { hotCleanupHooks(); if (typeof nextCurrentHook !== 'undefined' && typeof ReactCurrentDispatcher$1 !== 'undefined') { nextCurrentHook = current !== null ? current.memoizedState : null; if (typeof firstCurrentHook !== 'undefined') { firstCurrentHook = nextCurrentHook; } ReactCurrentDispatcher$1.current = nextCurrentHook === null ? HooksDispatcherOnMountInDEV : HooksDispatcherOnUpdateInDEV; } var rendered = render(); hotCleanupHooks(); return rendered; } var setHotElementComparator = function (newComparator) { hotCompareElements = newComparator }; var setHotTypeResolver = function (newResolver) { hotResolveType = newResolver; }; `; const CJS = ` ${reactHotLoaderCode}; var ReactDOM = { evalInReactContext: evalInReactContext, hotCleanup: hotCleanup, hotRenderWithHooks: hotRenderWithHooks, setHotElementComparator: setHotElementComparator, setHotTypeResolver: setHotTypeResolver, `; const commonJSEnd = ['var ReactDOM = {', CJS]; const commonJSEndCompact = ['var ReactDOM={', CJS]; const ESM = ` ${reactHotLoaderCode}; exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; exports.evalInReactContext= evalInReactContext, exports.hotCleanup= hotCleanup, exports.hotRenderWithHooks= hotRenderWithHooks, exports.setHotElementComparator= setHotElementComparator, exports.setHotTypeResolver= setHotTypeResolver, `; const esmEnd = ['exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals;', ESM]; const injectionEnd = { '16.13': esmEnd, '16.10': commonJSEnd, '16.9': commonJSEnd, '16.6': commonJSEnd, '16.4': commonJSEnd, '16.6-compact': commonJSEndCompact, '16.4-compact': commonJSEndCompact, }; const sign = '/* 🔥 this is hot-loader/react-dom 🔥 */'; function additionalTransform(source) { for (const key in additional) { source = source.split(additional[key][0]).join(additional[key][1]); } return source; } function transform(source) { if (source.indexOf('reconcileSingleElement') < 0) { // early reject return source; } if (source.indexOf(sign) >= 0) { // already patched return source; } for (const key in injectionStart) { if (source.indexOf(injectionStart[key][0]) > 0 && source.indexOf(injectionEnd[key][0]) > 0) { const result = additionalTransform( source .replace(injectionStart[key][0], injectionStart[key][1]) .replace(injectionEnd[key][0], injectionEnd[key][1]), ); return `${sign}\n${result}\n${sign}`; } } return source; } module.exports = transform; ================================================ FILE: src/webpack/webpackTagCommonJSExports.js ================================================ /* eslint-disable global-require, import/no-unresolved, no-var, camelcase, func-names, no-void */ /* global __FILENAME__, reactHotLoaderGlobal */ void (function register() { // eslint-disable-line no-extra-semi /* react-hot-loader/webpack */ var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } /* eslint-disable camelcase, no-undef */ const webpackExports = typeof __webpack_exports__ !== 'undefined' ? __webpack_exports__ : exports; /* eslint-enable camelcase, no-undef */ if (!webpackExports) { return; } if (typeof webpackExports === 'function') { reactHotLoader.register(webpackExports, 'module.exports', __FILENAME__); return; } /* eslint-disable no-restricted-syntax */ for (const key in webpackExports) { /* eslint-enable no-restricted-syntax */ if (!Object.prototype.hasOwnProperty.call(webpackExports, key)) { continue; } let namedExport; try { namedExport = webpackExports[key]; } catch (err) { continue; } reactHotLoader.register(namedExport, key, __FILENAME__); } })(); ================================================ FILE: test/.eslintrc.js ================================================ module.exports = { env: { jest: true }, }; ================================================ FILE: test/AppContainer.dev.test.js ================================================ /* eslint-env browser */ import 'babel-polyfill'; import React, { Component, PureComponent } from 'react'; import createReactClass from 'create-react-class'; import { mount } from 'enzyme'; import TestRenderer from 'react-test-renderer'; import { mapProps } from 'recompose'; import { polyfill } from 'react-lifecycles-compat'; import { AppContainer } from '../src/index.dev'; import RHL from '../src/reactHotLoader'; import { closeGeneration, configureGeneration, increment as incrementGeneration } from '../src/global/generation'; import { configureComponent } from '../src/utils.dev'; import configuration from '../src/configuration'; describe(`AppContainer (dev)`, () => { beforeEach(() => { RHL.reset(); configureGeneration(1, 1); }); describe('with class root', () => { it('renders children', () => { const spy = jest.fn(); class App extends Component { render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.find('App').length).toBe(1); expect(wrapper.contains(
hey
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); }); it('force updates the tree on receiving new children', () => { const spy = jest.fn(); const spy2 = jest.fn(); class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(spy).toHaveBeenCalledTimes(1); { class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); spy2(); return
ho
; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(spy).toHaveBeenCalledTimes(3); expect(spy2).toHaveBeenCalledTimes(2); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('force updates the tree on receiving cached children', () => { const spy = jest.fn(); class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const element = ; const wrapper = mount({element}); expect(spy).toHaveBeenCalledTimes(1); { class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); return
ho
; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: element }); } expect(spy).toHaveBeenCalledTimes(3); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('renders latest children on receiving cached never-rendered children', () => { const spy = jest.fn(); class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const element = ; let wrapper; { class App extends Component { shouldComponentUpdate() { return false; } render() { spy(); return
ho
; } } RHL.register(App, 'App', 'test.js'); wrapper = mount({element}); } expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.contains(
ho
)).toBe(true); }); it('hot-reloads children without losing state', () => { class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } render() { return
old render + {this.state.value} state
; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.text()).toBe('old render + old state'); { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } render() { return
new render + {this.state.value} state
; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(wrapper.text()).toBe('new render + old state'); }); it('Could handle render as a prop', () => { const spy = jest.fn(); /* eslint-disable */ class SubApp extends Component { componentWillUnmount() { spy('SubApp1'); } render = function() { return works; }; } class App extends Component { componentWillUnmount() { spy('App1'); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } render() { return ( before ); } } /* eslint-enable */ const Indirect = ({ App }) => ( ); configureComponent(Indirect, { allowSFC: false }); const wrapper = mount(); expect(wrapper.text()).toBe('works before'); { /* eslint-disable */ class SubApp extends Component { componentWillUnmount() { spy('SubApp2'); } render = function() { return works; }; } SubApp.displayName = 'SubApp'; class App extends Component { componentWillUnmount() { spy('App2'); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } render = function() { return ( after ); }; } App.displayName = 'App'; /* eslint-enable */ incrementGeneration(); wrapper.setProps({ App }); } expect(wrapper.text()).toBe('works after'); expect(spy).not.toHaveBeenCalled(); }); it('Could handle render as a prop with Pure Render', () => { const { pureRender } = configuration; configuration.pureRender = true; const spy = jest.fn(); /* eslint-disable */ class SubApp extends Component { componentWillUnmount() { spy('SubApp1'); } render = function() { return works; }; } class App extends Component { componentWillUnmount() { spy('App1'); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } render() { return ( before ); } } /* eslint-enable */ const Indirect = ({ App }) => ( ); configureComponent(Indirect, { allowSFC: false }); const wrapper = mount(); expect(wrapper.text()).toBe('works before'); expect(.type.prototype.render).toBe(App.prototype.render); const originalRender = App.prototype.render; let newRender; { /* eslint-disable */ class SubApp extends Component { componentWillUnmount() { spy('SubApp2'); } render = function() { return works; }; } SubApp.displayName = 'SubApp'; class App extends Component { componentWillUnmount() { spy('App2'); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } render() { return ( after ); } } App.displayName = 'App'; /* eslint-enable */ incrementGeneration(); wrapper.setProps({ App }); newRender = App.prototype.render; } expect(wrapper.text()).toBe('works after'); expect(spy).not.toHaveBeenCalled(); // render on App is changed by merge process. Compare with stored value expect(.type.prototype.render).not.toBe(originalRender); expect(.type.prototype.render).not.toBe(newRender); closeGeneration(); expect(.type.prototype.render).toBe(newRender); configuration.pureRender = pureRender; }); it('replaces children class methods', () => { const spy = jest.fn(); class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } handleClick() { spy('foo'); } render() { return old render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('foo'); expect(wrapper.text()).toBe('old render + old state'); spy.mockReset(); { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } handleClick() { spy('bar'); } render() { return new render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('bar'); expect(wrapper.text()).toBe('new render + old state'); }); it('replaces children class property arrow functions', () => { const spy = jest.fn(); class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } handleClick = () => { spy('foo'); }; render() { return old render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('foo'); expect(wrapper.text()).toBe('old render + old state'); spy.mockReset(); window.spy = spy; { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } handleClick = () => { window.spy('bar'); }; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return new render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('bar'); expect(wrapper.text()).toBe('new render + old state'); }); it('replaces children class arrow functions in constructor', () => { const spy = jest.fn(); class App extends Component { constructor(props) { super(props); this.handleClick = () => { spy('foo'); }; this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } render() { return old render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('foo'); expect(wrapper.text()).toBe('old render + old state'); spy.mockReset(); window.spy = spy; { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } handleClick = () => { window.spy('bar'); }; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return new render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('bar'); expect(wrapper.text()).toBe('new render + old state'); }); it('replaces children class property arrow functions without block statement bodies', () => { const spy = jest.fn(); class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } handleClick = () => spy('foo'); render() { return old render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('foo'); expect(wrapper.text()).toBe('old render + old state'); spy.mockReset(); window.spy = spy; { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } handleClick = () => window.spy('bar'); /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return new render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('bar'); expect(wrapper.text()).toBe('new render + old state'); }); it('replaces PureComponent', () => { const spy = jest.fn(); class Pure extends PureComponent { componentWillUnmount() { spy(); } render() { return I am old; } } RHL.register(Pure, 'Pure', 'test.js'); class RenderFn extends PureComponent { render() { const { _children, v } = this.props; return _children()(v); } } const innerRenderFn = v => ; const renderFn = () => innerRenderFn; class App extends PureComponent { render() { return (
); } } const wrapper = mount( , ); expect(wrapper.text()).toBe('I am old'); { class Pure extends PureComponent { componentWillUnmount() { spy(); } render() { return I am new; } } RHL.register(Pure, 'Pure', 'test.js'); wrapper.setProps({ children: }); } expect(wrapper.text()).toBe('I am new'); expect(spy).not.toHaveBeenCalled(); }); it('replaces React.memo', done => { if (!React.memo) { expect(1).toBe(1); done(); return; } const Pure = React.memo(() => I am old); RHL.register(Pure, 'Pure', 'test.js'); const App = () => (
); const wrapper = TestRenderer.create( , ); expect(wrapper.root.findByProps({ children: 'I am old' })).toBeDefined(); { const Pure = React.memo(() => I am new); RHL.register(Pure, 'Pure', 'test.js'); wrapper.update( , ); } setTimeout(() => { expect(wrapper.root.findByProps({ children: 'I am new' })).toBeDefined(); done(); }, 16); }); it('handles forwardRef', () => { if (!React.forwardRef) { expect(1).toBe(1); return; } const spy = jest.fn(); class MountSpy extends PureComponent { static displayName = 'MountSpy'; componentWillUnmount() { spy(); } render() { return I am old; } } const FRW = React.forwardRef(() => ( )); RHL.register(FRW, 'FRW', 'test.js'); const App = () => (
children
); const wrapper = TestRenderer.create( , ); expect(wrapper.root.findByProps({ children: 'I am old' })).toBeDefined(); { class MountSpy extends PureComponent { static displayName = 'MountSpy'; componentWillUnmount() { spy(); } render() { return I am new; } } const FRW = React.forwardRef(() => ( )); RHL.register(FRW, 'FRW', 'test.js'); wrapper.update( , ); } expect(spy).not.toHaveBeenCalled(); expect(wrapper.root.findByProps({ children: 'I am new' })).toBeDefined(); }); it.skip('handles react-lazy', async () => { const spy = jest.fn(); class MountSpy extends PureComponent { static displayName = 'MountSpy'; componentWillUnmount() { spy(); } render() { return I am old; } } const Tmp = MountSpy; const Lazy = React.lazy(() => Promise.resolve({ default: Tmp })); RHL.register(MountSpy, 'Pure', 'test.js'); RHL.register(Lazy, 'Lazy', 'test.js'); const ref = jest.fn(); class App extends Component { render() { return ; } } const wrapper = TestRenderer.create( , ); await Promise.resolve(1); expect(spy).not.toHaveBeenCalled(); expect(ref).toHaveBeenCalledTimes(1); expect(wrapper.root.findByProps({ children: 'I am old' })).toBeDefined(); { class MountSpy extends PureComponent { static displayName = 'MountSpy'; componentWillUnmount() { spy(); } render() { return I am new; } } const Lazy = React.lazy(() => Promise.resolve({ default: Tmp })); RHL.register(MountSpy, 'Pure', 'test.js'); RHL.register(Lazy, 'Lazy', 'test.js'); wrapper.update( , ); await Promise.resolve(1); } expect(spy).not.toHaveBeenCalled(); expect(wrapper.root.findByProps({ children: 'I am new' })).toBeDefined(); }); it('replaces children with class property arrow functions with different numbers of arguments', () => { const spy = jest.fn(); class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } handleClick = () => spy('foo'); render() { return old render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); wrapper.find('span').simulate('click'); expect(spy).toHaveBeenCalledWith('foo'); expect(wrapper.text()).toBe('old render + old state'); spy.mockReset(); window.spy = spy; { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } handleClick = ({ target }) => window.spy(target.value); /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return new render + {this.state.value} state; } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } wrapper.find('span').simulate('click', { target: { value: 'bar' } }); expect(spy).toHaveBeenCalledWith('bar'); expect(wrapper.text()).toBe('new render + old state'); }); it('passes a default context value', () => { if (React.version.startsWith('16')) { const spy = jest.fn(); const contextValue = 'value'; const { Consumer } = React.createContext('value'); class App extends Component { render() { return ( {value => { spy(); return
{value}
; }}
); } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.contains(
{contextValue}
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); } else { expect(true).toBe(true); } }); it('passes provider defined context value', () => { if (React.version.startsWith('16')) { const spy = jest.fn(); const contextValue = 'value'; const { Provider, Consumer } = React.createContext(); class App extends Component { render() { return ( {value => { spy(); return
{value}
; }}
); } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.contains(
{contextValue}
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); } else { expect(true).toBe(true); } }); it('passes provider defined falsy context value', () => { if (React.version.startsWith('16')) { const spy = jest.fn(); const contextValue = 0; const { Provider, Consumer } = React.createContext(); class App extends Component { render() { return ( {value => { spy(value); return
{value}
; }}
); } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.contains(
{contextValue}
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith(contextValue); } else { expect(true).toBe(true); } }); it('passes provider defined falsy context value on receiving new children', () => { if (React.version.startsWith('16')) { const spy = jest.fn(); const spy2 = jest.fn(); const contextValue = false; const { Provider, Consumer } = React.createContext(); class App extends Component { render() { return ( {value => { spy(value); return
{value}
; }}
); } } RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.contains(
{contextValue}
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith(contextValue); { class App extends Component { shouldComponentUpdate() { return false; } render() { return ( {value => { spy2(value); return
{value}
; }}
); } } RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } const firstCallArg = spy2.mock.calls[0][0]; expect(spy2).toHaveBeenCalledTimes(2); expect(firstCallArg).toBeDefined(); expect(firstCallArg).toBe(contextValue); } else { expect(true).toBe(true); } }); }); describe('with createClass root', () => { it('renders children', () => { const spy = jest.fn(); const App = createReactClass({ render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.find('App').length).toBe(1); expect(wrapper.contains(
hey
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); }); it('force updates the tree on receiving new children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(spy).toHaveBeenCalledTimes(1); { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(spy).toHaveBeenCalledTimes(1 + 2); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('force updates the tree on receiving cached children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const element = ; const wrapper = mount({element}); expect(spy).toHaveBeenCalledTimes(1); { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: element }); } expect(spy).toHaveBeenCalledTimes(1 + 2); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('renders latest children on receiving cached never-rendered children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const element = ; let wrapper; { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); wrapper = mount({element}); } expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.contains(
ho
)).toBe(true); }); it('hot-reloads children without losing state', () => { const App = createReactClass({ getInitialState() { return { value: 'old' }; }, shouldComponentUpdate() { return false; }, render() { return
old render + {this.state.value} state
; }, }); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.text()).toBe('old render + old state'); { const App = createReactClass({ getInitialState() { return { value: 'new' }; }, shouldComponentUpdate() { return false; }, render() { return
new render + {this.state.value} state
; }, }); RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(wrapper.text()).toBe('new render + old state'); }); }); describe('with createFactory root', () => { it('renders children', () => { const spy = jest.fn(); const App = createReactClass({ render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); const wrapper = mount({AppF()}); expect(wrapper.find('App').length).toBe(1); expect(wrapper.contains(
hey
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); }); it('force updates the tree on receiving new children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); const wrapper = mount({AppF()}); expect(spy).toHaveBeenCalledTimes(1); { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); wrapper.setProps({ children: AppF() }); } expect(spy).toHaveBeenCalledTimes(1 + 2); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('force updates the tree on receiving cached children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); const element = AppF(); const wrapper = mount({element}); expect(spy).toHaveBeenCalledTimes(1); { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: element }); } expect(spy).toHaveBeenCalledTimes(1 + 2); expect(wrapper.update().contains(
ho
)).toBe(true); }); it('renders latest children on receiving cached never-rendered children', () => { const spy = jest.fn(); const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
hey
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); const element = AppF(); let wrapper; { const App = createReactClass({ shouldComponentUpdate() { return false; }, render() { spy(); return
ho
; }, }); RHL.register(App, 'App', 'test.js'); wrapper = mount({element}); } expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.contains(
ho
)).toBe(true); }); it('hot-reloads children without losing state', () => { const App = createReactClass({ getInitialState() { return { value: 'old' }; }, shouldComponentUpdate() { return false; }, render() { return
old render + {this.state.value} state
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); const wrapper = mount({AppF()}); expect(wrapper.text()).toBe('old render + old state'); { const App = createReactClass({ getInitialState() { return { value: 'new' }; }, shouldComponentUpdate() { return false; }, render() { return
new render + {this.state.value} state
; }, }); RHL.register(App, 'App', 'test.js'); const AppF = React.createFactory(App); wrapper.setProps({ children: AppF() }); } expect(wrapper.text()).toBe('new render + old state'); }); }); describe('with SFC root', () => { it('renders children', () => { const spy = jest.fn(); const App = () => { spy(); return
hey
; }; RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.find('App').length).toBe(1); expect(wrapper.contains(
hey
)).toBe(true); expect(spy).toHaveBeenCalledTimes(1); }); it('renders falsy children', () => { const spy = jest.fn(); const App = () => { spy(); return null; }; RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(wrapper.find('App').length).toBe(1); expect(spy).toHaveBeenCalledTimes(1); }); it('force updates the tree on receiving new children', () => { const spy = jest.fn(); const App = () => { spy(); return
hey
; }; RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(spy).toHaveBeenCalledTimes(1); { const App = () => { spy(); return
ho
; }; RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(spy).toHaveBeenCalledTimes(configuration.pureRender && !React.lazy ? 4 : 3); expect(wrapper.contains(
ho
)).toBe(true); }); it('force updates the tree on receiving cached children', () => { const firstSpy = jest.fn(); const Dummy = () => { firstSpy(); return
first
; }; const App = () => ; RHL.register(Dummy, 'Dummy', 'test.js'); const wrapper = mount( , ); expect(firstSpy).toHaveBeenCalledTimes(1); const secondSpy = jest.fn(); { const Dummy = () => { secondSpy(); return
second
; }; RHL.register(Dummy, 'Dummy', 'test.js'); wrapper.setProps({ children: }); } expect(firstSpy).toHaveBeenCalledTimes(1); expect(secondSpy).toHaveBeenCalledTimes(configuration.pureRender && !React.lazy ? 3 : 2); expect(wrapper.contains(
second
)).toBe(true); }); it('renders latest children on receiving cached never-rendered children', () => { const spy = jest.fn(); const App = () => { spy(); return
hey
; }; RHL.register(App, 'App', 'test.js'); const element = ; let wrapper; { const App = () => { spy(); return
ho
; }; RHL.register(App, 'App', 'test.js'); wrapper = mount({element}); } expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.contains(
ho
)).toBe(true); }); it('hot-reloads children without losing state', () => { class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } render() { return
old render + {this.state.value} state
; } } RHL.register(App, 'App', 'test.js'); const Root = () => ; RHL.register(Root, 'Root', 'test.js'); const wrapper = mount( , ); expect(wrapper.text()).toBe('old render + old state'); { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } render() { return
new render + {this.state.value} state
; } } RHL.register(App, 'App', 'test.js'); const Root = () => ; RHL.register(Root, 'Root', 'test.js'); wrapper.setProps({ children: }); } expect(wrapper.text()).toBe('new render + old state'); }); }); it('hot-reloads nested children without losing state', () => { const onUnmount = jest.fn(); let child = 1; function getChild() { if (child === 1) { return class Child extends Component { componentWillUnmount() { onUnmount(); } render() { return
old child
; } }; } return class Child extends Component { componentWillUnmount() { // onUnmount(); } render() { return
new child
; } }; } class Layout extends Component { render() { return (

TEST

{this.props.children}
); } } class App extends Component { render() { const Child = getChild(); return ( ); } } const Root = () => ; RHL.register(Root, 'Root', 'test.js'); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(onUnmount).toHaveBeenCalledTimes(0); child = 2; // emulate HRM incrementGeneration(); wrapper.setProps({ children: }); expect(onUnmount).toHaveBeenCalledTimes(0); expect(wrapper.text()).toBe('TESTnew childnew child'); }); it('hot-reloads nested SFC children without losing state', () => { const onUnmount = jest.fn(); class MountSpy extends Component { componentWillUnmount() { onUnmount(); } render() { return
spy
; } } let child = 1; const childs = [ function Child() { return (
a
); }, function Child() { return (
b
); }, ]; function getChild() { return child === 1 ? childs[0] : childs[1]; } class Layout extends Component { render() { return (

TEST

{this.props.children}
); } } class App extends Component { render() { const Child = getChild(); return ( ); } } const Root = () => ; RHL.register(Root, 'Root', 'test.js'); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(onUnmount).toHaveBeenCalledTimes(0); expect(wrapper.text()).toBe('TESTa spya spy'); child = 2; // emulate HRM incrementGeneration(); wrapper.setProps({ children: }); expect(onUnmount).toHaveBeenCalledTimes(0); expect(wrapper.text()).toBe('TESTb spyb spy'); }); it('unmounts nested in mixed condition', () => { const onUnmount = jest.fn(); class MountSpy extends Component { componentWillUnmount() { onUnmount(); } render() { return
spy
; } } let child = 1; const childA = () => (
a
); class ChildB extends Component { render() { return (
b
); } } function getChild() { return child === 1 ? childA : ChildB; } class Layout extends Component { render() { return (

TEST

{this.props.children}
); } } class App extends Component { render() { const Child = getChild(); return ( ); } } const Root = () => ; RHL.register(Root, 'Root', 'test.js'); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); expect(onUnmount).toHaveBeenCalledTimes(0); expect(wrapper.text()).toBe('TESTa spya spy'); child = 2; // emulate HRM incrementGeneration(); wrapper.setProps({ children: }); expect(onUnmount).toHaveBeenCalledTimes(2); expect(wrapper.text()).toBe('TESTb spyb spy'); }); it('renders children with chunked re-register', () => { const spy = jest.fn(); class App extends Component { componentWillUnmount() { spy(); } render() { return
I AM CHILD
; } } RHL.reset(); RHL.register(App, 'App1', 'test.js'); RHL.register(App, 'App2', 'test.js'); const wrapper = mount( , ); expect(spy).not.toHaveBeenCalled(); wrapper.setProps({ children: }); expect(spy).not.toHaveBeenCalled(); RHL.register(App, 'App3', 'test.js'); wrapper.setProps({ children: }); expect(spy).not.toHaveBeenCalled(); { class App extends Component { componentWillUnmount() { spy(); } render() { return
I AM NEW CHILD
; } } RHL.register(App, 'App3', 'test.js'); wrapper.setProps({ children: }); expect(spy).not.toHaveBeenCalled(); RHL.register(App, 'App4', 'test.js'); wrapper.setProps({ children: }); expect(spy).not.toHaveBeenCalled(); } }); describe('test similarity', () => { describe('unmounts nested in mixed condition', () => { /* eslint-disable */ let onUnmount = jest.fn(); class MountSpy extends Component { componentWillUnmount() { onUnmount(); } render() { return
spy
; } } let ChildBase, ChildB, ChildC, ChildD, ChildE, ChildF; const generateChilds = () => { ChildBase = class ChildA extends Component { render() { return (
b
); } }; { ChildB = class ChildA extends Component { render() { return (
NOT A BIG CHANGE!
); } }; } { ChildC = class ChildA extends Component { componentWillUnmount() { // some data } render() { return (
b
); } }; } { ChildD = class ChildA extends Component { componentWillUnmount() { // some data } componentWillMount() { // some data } render() { return (
BIG CHANGE!
); } }; } { ChildE = class ChildA extends Component { render() { return (
SUPER MEGA UBER PUPER BIG CHANGE!
); } }; } { ChildF = function ChildA() { return (
b
); }; } }; /* eslint-enable */ const expectUnmounts = [false, false, true, true, true]; generateChilds(); [ChildB, ChildC, ChildD, ChildE, ChildF].forEach((Replace, index) => { const expectUnmount = expectUnmounts[index]; it(`${expectUnmount ? 'expect unmount' : 'keep'} for ${index}`, () => { generateChilds(); // renegerate base onUnmount = jest.fn(); let Child = ChildBase; class App extends Component { render() { return (
); } } const wrapper = mount( , ); expect(onUnmount).toHaveBeenCalledTimes(0); // Replace.name=Child.name; Child = Replace; incrementGeneration(); wrapper.setProps({ children: }); expect(onUnmount).toHaveBeenCalledTimes(expectUnmount ? 1 : 0); }); }); }); }); describe('with HOC-wrapped root', () => { it('renders children', () => { const spy = jest.fn(); class App extends React.Component { render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); const wrapper = mount( , ); expect(wrapper.find('App').length).toBe(1); expect(wrapper.contains(
hey
)).toBe(true); expect(wrapper.find('App').prop('n')).toBe(15); expect(spy).toHaveBeenCalledTimes(1); }); it('force updates the tree on receiving new children', () => { const spy = jest.fn(); class App extends React.Component { render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); const wrapper = mount( , ); expect(spy).toHaveBeenCalledTimes(1); { class App extends React.Component { render() { spy(); return
ho
; } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); wrapper.setProps({ children: }); } if (React.memo && !configuration.pureRender) { expect(spy).toHaveBeenCalledTimes(1 + 2); } expect(wrapper.contains(
ho
)).toBe(true); }); it('force updates the tree on receiving cached children', () => { const firstSpy = jest.fn(); const Dummy = () => { firstSpy(); return
first
; }; const App = () => ; RHL.register(Dummy, 'Dummy', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); const wrapper = mount( , ); expect(firstSpy).toHaveBeenCalledTimes(1); const secondSpy = jest.fn(); { const Dummy = () => { secondSpy(); return
second
; }; RHL.register(Dummy, 'Dummy', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); wrapper.setProps({ children: }); } expect(firstSpy).toHaveBeenCalledTimes(1); expect(secondSpy).toHaveBeenCalledTimes(configuration.pureRender && !React.lazy ? 3 : 2); expect(wrapper.contains(
second
)).toBe(true); }); it('support indeterminateComponent', () => { [false, true].forEach(withContext => { const spy = jest.fn(); const AnotherComponent = () =>
old
; class App extends React.Component { static getDerivedStateFromProps({ n }) { return { n }; } render() { return (
hey {this.state.n}
); } } // Can't always run polyfill, as running polyfill in React 16 will make // this test case pass even without the fix. if (React.version.startsWith('15')) { polyfill(App); } let CurrentApp = App; RHL.register(AnotherComponent, 'AnotherComponent', 'test.js'); RHL.register(App, 'App', 'test.js'); // return rendered component from a stateless const IndeterminateComponent = (props, context) => { const instance = new CurrentApp(props, context); IndeterminateComponent.getDerivedStateFromProps = CurrentApp.getDerivedStateFromProps; return instance; }; if (withContext) { IndeterminateComponent.contextTypes = {}; } const RootApp = props => ; const wrapper = mount( , ); expect(wrapper.text()).toBe('hey 42 old'); { class App2 extends React.Component { static getDerivedStateFromProps({ n }) { return { n }; } render() { spy(); return
ho {this.state.n + 1}
; } } if (React.version.startsWith('15')) { polyfill(App2); } RHL.register(App2, 'App', 'test.js'); CurrentApp = App2; const AnotherComponent = () =>
new
; RHL.register(AnotherComponent, 'AnotherComponent', 'test.js'); wrapper.setProps({ update: true, children: }); // How it works. IndeterminateComponent calculates return value once. expect(wrapper.text()).toBe('hey 44 new'); expect(spy).toHaveBeenCalledTimes(0); // never gets called // How it should work // expect(wrapper.text()).toBe('ho 45 new'); // expect(spy).toHaveBeenCalledTimes(2); } }); }); it('renders latest children on receiving cached never-rendered children', () => { const spy = jest.fn(); class App extends React.Component { render() { spy(); return
hey
; } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); const element = ; let wrapper; { class App extends React.Component { render() { spy(); return
ho
; } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); wrapper = mount({element}); } expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.contains(
ho
)).toBe(true); }); it('hot-reloads children with overriden render', () => { class App extends Component { constructor() { super(); this.secret = 1; this.superSecret = 42; } componentWillMount() { const oldRender = this.render.bind(this); this.render = () => { this.superSecret = this.secret + 1; return
PATCHED + {oldRender()}
; }; } render() { return
{this.superSecret}
; } } RHL.register(App, 'App', 'test.js'); const MiddleApp = () => (
); const wrapper = mount( , ); expect(wrapper.text()).toBe('PATCHED + 2'); { class App2 extends Component { constructor() { super(); this.secret = 2; } render() { return
{this.superSecret * 2} v2
; } } RHL.register(App2, 'App', 'test.js'); wrapper.setProps({ children: }); } if (!configuration.pureRender) { expect(wrapper.update().text()).toBe('PATCHED + 6 v2'); } }); it('hot-reloads children inside simple Fragments', () => { if (React.version.startsWith('16')) { const unmount = jest.fn(); class InnerComponent extends Component { componentWillUnmount() { unmount(); } render() { return internal; } } InnerComponent.displayName = 'InnerComponent'; const App = () => ( ); RHL.register(App, 'App', 'test.js'); const wrapper = mount( , ); { class InnerComponent extends Component { componentWillUnmount() { unmount(); } render() { return internal; } } InnerComponent.displayName = 'InnerComponent'; const App = () => ( ); RHL.register(App, 'App', 'test.js'); wrapper.setProps({ children: }); } expect(unmount).toHaveBeenCalledTimes(0); expect(wrapper.update().text()).toBe('another text internal'); } else { // React 15 is always ok expect(true).toBe(true); } }); it('hot-reloads children inside Fragments', () => { if (React.version.startsWith('16')) { const unmount = jest.fn(); class InnerComponent extends Component { componentWillUnmount() { unmount(); } render() { return
OldInnerComponent
; } } InnerComponent.displayName = 'InnerComponent'; const InnerItem = () => ( {false &&
hole
} -1- {false &&
hole
} -3-
); RHL.register(InnerItem, 'InnerItem', 'test.js'); const Item = () => (
  • 1
  • 3
  • F
    ); // const App = () => (
    ); const wrapper = mount( , ); expect(wrapper.update().text()).toBe('1-1-OldInnerComponent-3-OldInnerComponent3F'); { class InnerComponent extends Component { componentWillUnmount() { unmount(); } render() { return
    NewInnerComponent
    ; } } InnerComponent.displayName = 'InnerComponent'; const InnerItem = () => ( {false &&
    hole
    } -2- {false &&
    hole
    } -3- *
    ); RHL.register(InnerItem, 'InnerItem', 'test.js'); wrapper.setProps({ children: }); } expect(unmount).toHaveBeenCalledTimes(0); expect(wrapper.update().text()).toBe( '1-2-NewInnerComponent-3-NewInnerComponent*3F', // it should not be so! ); } else { // React 15 is always ok expect(true).toBe(true); } }); it('hot-reloads children without losing state', () => { class App extends Component { constructor(props) { super(props); this.state = { value: 'old' }; } shouldComponentUpdate() { return false; } render() { return (
    old render + {this.state.value} state + {this.props.n}
    ); } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); const wrapper = mount( , ); expect(wrapper.text()).toBe('old render + old state + 15'); { class App extends Component { constructor(props) { super(props); this.state = { value: 'new' }; } shouldComponentUpdate() { return false; } render() { return (
    new render + {this.state.value} state + {this.props.n}
    ); } } RHL.register(App, 'App', 'test.js'); const Enhanced = mapProps(props => ({ n: props.n * 5 }))(App); RHL.register(Enhanced, 'Enhanced', 'test.js'); wrapper.setProps({ children: }); } expect(wrapper.text()).toBe('new render + old state + 20'); }); it('should work with react-lifecycle-compact', () => { class Component extends React.Component { static getDerivedStateFromProps() { return {}; } } /* eslint-disable no-underscore-dangle */ polyfill(Component); expect(Component.prototype.componentWillReceiveProps.__suppressDeprecationWarning).toBe(true); const Proxy = .type; expect(Proxy.prototype.componentWillReceiveProps.__suppressDeprecationWarning).toBe(true); /* eslint-enable */ }); }); }); ================================================ FILE: test/__babel_fixtures__/bindings.js ================================================ import P, { Q } from "left-pad"; const A = 42; function B() { function R() {} class S {} const T = 42; } export class C { U() { function V() { class W {} } } } const D = class X {}; let E = D; var Y = require("left-pad"); var { Z } = require("left-pad"); export default React.createClass({}); ================================================ FILE: test/__babel_fixtures__/class-properties/arguments.js ================================================ class Foo { bar = (a, b) => { arguments; return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/arrow-function-in-constructor.js ================================================ class Foo { constructor() { this.onClick = (e) => e.target.value } } ================================================ FILE: test/__babel_fixtures__/class-properties/async-functions-expression-body.js ================================================ class Foo { bar = async (a, b) => await b(a) } ================================================ FILE: test/__babel_fixtures__/class-properties/async-functions.js ================================================ class Foo { bar = async (a, b) => { return await a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/block-body.js ================================================ class Foo { bar = (a, b) => { return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/default-params.js ================================================ class Foo { bar = (a = "foo") => { return `${a}bar`; }; } ================================================ FILE: test/__babel_fixtures__/class-properties/destructured-params.js ================================================ class Foo { bar = ({ a }, { b }) => { return `${a}${b}`; }; } ================================================ FILE: test/__babel_fixtures__/class-properties/expression-body.js ================================================ class Foo { onClick = (e) => e.target.value } ================================================ FILE: test/__babel_fixtures__/class-properties/nested-arguments.js ================================================ class Foo { bar = (a, b) => { () => { arguments; } return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/nested-new.target.js ================================================ class Foo { bar = (a, b) => { () => { new.target; } return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/new.target.js ================================================ class Foo { bar = (a, b) => { new.target; return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/not-a-function.js ================================================ class Foo { bar = 2; } ================================================ FILE: test/__babel_fixtures__/class-properties/not-an-arrow-function.js ================================================ class Foo { bar = function (a, b) { return a(b); }; } ================================================ FILE: test/__babel_fixtures__/class-properties/same-name-as-class-method.js ================================================ class Foo { bar = (a, b) => { return a(b); }; bar() { return 2 } } ================================================ FILE: test/__babel_fixtures__/class-properties/static-property.js ================================================ class Foo { static bar = (a, b) => { return a(b); }; } ================================================ FILE: test/__babel_fixtures__/counter.js ================================================ class Counter { } export default () =>
    ================================================ FILE: test/__babel_fixtures__/drop-hot-half.prod.js ================================================ import { hot } from 'react-hot-loader'; import { hot as rootHot } from 'react-hot-loader/root'; const control = compose( withDebug, withDebug, )(App); const targetCase1 = compose( withDebug, withDebug, hot(module), )(App); const targetCase2 = compose( withDebug, withDebug, rootHot, )(App); const removeHot1 = hot(control); const removeHot2 = hot(module)(control); const removeHot3 = rootHot(control); ================================================ FILE: test/__babel_fixtures__/drop-hot.prod.js ================================================ import React from 'react' import { hot, foo } from 'react-hot-loader' import { hot as namedHot, foo as namedFoo } from 'react-hot-loader' import { hot as rootHot } from 'react-hot-loader/root' import { hot as namedHot2 } from 'react-hot-loader' import { hot as notRHLHot } from 'not-react-hot-loader' import * as RHL from 'react-hot-loader' import * as RHL2 from 'react-hot-loader' import * as NOTRHL from 'not-react-hot-loader' const App = () =>
    Hello World!
    const a = hot(module)(App); const z = rootHot(App); const b = namedHot(module)(App); const c = namedHot2(module)(App); const d = RHL.hot(module)(App); const e = RHL2.hot(module)(App); foo(module)(App); notRHLHot(module)(App); namedFoo(module)(App); RHL.foo(module)(App); NOTRHL.hot(module)(App); // should not drop incomplete reference namedFoo(module); export { a, b, c, d, e, z }; ================================================ FILE: test/__babel_fixtures__/hooks.js ================================================ import React, {useState} from 'react'; import {useExternalHook} from 'external-hook' const NoHooks = () =>
    no hooks
    ; const UseStateHook = () => { useState(42); }; const UseStateHooks = () => { useState(42); useState(24); }; const useEffectHook = () => { React.useEffect(() => ({})); useState(24); React.useState(42); }; const useForwardRefHook = React.forwardRef(() => { React.useEffect(() => ({})); useState(24); React.useState(42); }); const useForwardRefFunctionHook = React.forwardRef(function () { React.useEffect(() => ({})); useState(24); React.useState(42); }); const useCustomHook = () => { useState(42); useEffectHook(); useExternalHook(); }; const useCustomHookAgain = () => { useState(42); useEffectHook(); useExternalHook(); }; const useInnerHook = ({useHookFromProps}) => { const useHookBase = () => useState(); const useHook = () => useState(useHookFromProps(useHookBase())); useHookFromProps(); { // sub scope useHook(); } }; function useFunc () { useState(42); useEffectHook(); }; ================================================ FILE: test/__babel_fixtures__/issue-246.js ================================================ export function spread(...args) { return args.push(1) } ================================================ FILE: test/__babel_fixtures__/local-hooks.js ================================================ import {useState} from "react"; function Component1() { function useRippleHandler() {} useRippleHandler(); useRippleHandler(); } function Component2() { const useRippleHandler = () => {}; useRippleHandler(); useRippleHandler(); } function Component3() { const useRippleHandler = function () {}; useRippleHandler(); useRippleHandler(); } const useInnerHook = ({useHookFromProps}) => { const useHookBase = () => useState(); const useHook = () => useState(useHookFromProps(useHookBase())); useHookFromProps(); { // sub scope useHook(); } }; const OnlyThisOneUsesExternalHook = () => { useInnerHook(); useState(); }; // check for "return [" ================================================ FILE: test/__babel_fixtures__/name-clash.js ================================================ const _default = 10; export default 42; ================================================ FILE: test/__snapshots__/babel.test.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods arguments.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { var _arguments = arguments; _classCallCheck(this, Foo); this.bar = function (a, b) { _arguments; return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods arrow function in constructor.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.onClick = function (e) { return e.target.value; }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods async functions expression body.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step(\\"next\\", value); }, function (err) { step(\\"throw\\", err); }); } } return step(\\"next\\"); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { var _this = this; _classCallCheck(this, Foo); this.bar = function () { var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(a, b) { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return b(a); case 2: return _context.abrupt(\\"return\\", _context.sent); case 3: case \\"end\\": return _context.stop(); } } }, _callee, _this); })); return function (_x, _x2) { return _ref.apply(this, arguments); }; }(); } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods async functions.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step(\\"next\\", value); }, function (err) { step(\\"throw\\", err); }); } } return step(\\"next\\"); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { var _this = this; _classCallCheck(this, Foo); this.bar = function () { var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(a, b) { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return a(b); case 2: return _context.abrupt(\\"return\\", _context.sent); case 3: case \\"end\\": return _context.stop(); } } }, _callee, _this); })); return function (_x, _x2) { return _ref.apply(this, arguments); }; }(); } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods block body.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (a, b) { return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods default params.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function () { var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : \\"foo\\"; return a + \\"bar\\"; }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods destructured params.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (_ref, _ref2) { var a = _ref.a; var b = _ref2.b; return \\"\\" + a + b; }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods expression body.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.onClick = function (e) { return e.target.value; }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods nested arguments.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { var _arguments = arguments; _classCallCheck(this, Foo); this.bar = function (a, b) { (function () { _arguments; }); return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods nested new.target.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (a, b) { (function () { new.target; }); return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods new.target.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (a, b) { new.target; return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods not a function.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = 2; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods not an arrow function.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (a, b) { return a(b); }; } _createClass(Foo, [{ key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods same name as class method.js 1`] = ` "\\"use strict\\"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function () { function Foo() { _classCallCheck(this, Foo); this.bar = function (a, b) { return a(b); }; } _createClass(Foo, [{ key: \\"bar\\", value: function bar() { return 2; } }, { key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Foo; }(); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" copies arrow function body block onto hidden class methods static property.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Foo = function Foo() { _classCallCheck(this, Foo); }; Foo.bar = function (a, b) { return a(b); }; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components bindings.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.C = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\\"value\\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _leftPad = require(\\"left-pad\\"); var _leftPad2 = _interopRequireDefault(_leftPad); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var A = 42; function B() { function R() {} var S = function S() { _classCallCheck(this, S); }; var T = 42; } var C = exports.C = function () { function C() { _classCallCheck(this, C); } _createClass(C, [{ key: \\"U\\", value: function U() { function V() { var W = function W() { _classCallCheck(this, W); }; } } }, { key: \\"__reactstandin__regenerateByEval\\", // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return C; }(); var D = function X() { _classCallCheck(this, X); }; var E = D; var Y = require(\\"left-pad\\"); var _require = require(\\"left-pad\\"), Z = _require.Z; var _default = React.createClass({ displayName: \\"_default\\" }); exports.default = _default; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(A, \\"A\\", __FILENAME__); reactHotLoader.register(B, \\"B\\", __FILENAME__); reactHotLoader.register(C, \\"C\\", __FILENAME__); reactHotLoader.register(D, \\"D\\", __FILENAME__); reactHotLoader.register(E, \\"E\\", __FILENAME__); reactHotLoader.register(_default, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components counter.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } } var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var Counter = function Counter() { _classCallCheck(this, Counter); }; var _default = function _default() { return React.createElement( \\"div\\", null, React.createElement(Counter, null) ); }; exports.default = _default; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Counter, \\"Counter\\", __FILENAME__); reactHotLoader.register(_default, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components drop hot half.prod.js 1`] = ` "'use strict'; var _reactHotLoader = require('react-hot-loader'); var _root = require('react-hot-loader/root'); var control = compose(withDebug, withDebug)(App); var targetCase1 = compose(withDebug, withDebug, (0, _reactHotLoader.hot)(module))(App); var targetCase2 = compose(withDebug, withDebug, _root.hot)(App); var removeHot1 = (0, _reactHotLoader.hot)(control); var removeHot2 = control; var removeHot3 = control;" `; exports[`babel Targetting "es2015" tags potential React components drop hot.prod.js 1`] = ` "'use strict'; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.z = exports.e = exports.d = exports.c = exports.b = exports.a = undefined; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactHotLoader = require('react-hot-loader'); var RHL = _interopRequireWildcard(_reactHotLoader); var RHL2 = _interopRequireWildcard(_reactHotLoader); var _root = require('react-hot-loader/root'); var _notReactHotLoader = require('not-react-hot-loader'); var NOTRHL = _interopRequireWildcard(_notReactHotLoader); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var App = function App() { return _react2.default.createElement( 'div', null, 'Hello World!' ); }; var a = App; var z = App; var b = App; var c = App; var d = App; var e = App; (0, _reactHotLoader.foo)(module)(App); (0, _notReactHotLoader.hot)(module)(App); (0, _reactHotLoader.foo)(module)(App); RHL.foo(module)(App); NOTRHL.hot(module)(App); // should not drop incomplete reference (0, _reactHotLoader.foo)(module); exports.a = a; exports.b = b; exports.c = c; exports.d = d; exports.e = e; exports.z = z;" `; exports[`babel Targetting "es2015" tags potential React components hooks.js 1`] = ` "'use strict'; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _externalHook = require('external-hook'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var NoHooks = function NoHooks() { return _react2.default.createElement( 'div', null, 'no hooks' ); }; var UseStateHook = function UseStateHook() { (0, _react.useState)(42); }; __signature__(UseStateHook, 'useState{(42)}'); var UseStateHooks = function UseStateHooks() { (0, _react.useState)(42); (0, _react.useState)(24); }; __signature__(UseStateHooks, 'useState{(42)}\\\\nuseState{(24)}'); var useEffectHook = function useEffectHook() { _react2.default.useEffect(function () { return {}; }); (0, _react.useState)(24); _react2.default.useState(42); }; __signature__(useEffectHook, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}'); var useForwardRefHook = _react2.default.forwardRef(__signature__(function () { _react2.default.useEffect(function () { return {}; }); (0, _react.useState)(24); _react2.default.useState(42); }, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}')); var useForwardRefFunctionHook = _react2.default.forwardRef(__signature__(function () { _react2.default.useEffect(function () { return {}; }); (0, _react.useState)(24); _react2.default.useState(42); }, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}')); var useCustomHook = function useCustomHook() { (0, _react.useState)(42); useEffectHook(); (0, _externalHook.useExternalHook)(); }; __signature__(useCustomHook, 'useState{(42)}\\\\nuseEffectHook{}\\\\nuseExternalHook{}', function () { return [useEffectHook, _externalHook.useExternalHook]; }); var useCustomHookAgain = function useCustomHookAgain() { (0, _react.useState)(42); useEffectHook(); (0, _externalHook.useExternalHook)(); }; __signature__(useCustomHookAgain, 'useState{(42)}\\\\nuseEffectHook{}\\\\nuseExternalHook{}', function () { return [useEffectHook, _externalHook.useExternalHook]; }); var useInnerHook = function useInnerHook(_ref) { var useHookFromProps = _ref.useHookFromProps; var useHookBase = function useHookBase() { return (0, _react.useState)(); }; __signature__(useHookBase, 'useState{}'); var useHook = function useHook() { return (0, _react.useState)(useHookFromProps(useHookBase())); }; __signature__(useHook, 'useState{(useHookFromProps(useHookBase()))}\\\\nuseHookFromProps{}\\\\nuseHookBase{}', function () { return [useHookFromProps, useHookBase]; }); useHookFromProps(); { // sub scope useHook(); } }; __signature__(useInnerHook, 'useHookFromProps{}\\\\nuseHook{}'); function useFunc() { (0, _react.useState)(42); useEffectHook(); } __signature__(useFunc, 'useState{(42)}\\\\nuseEffectHook{}', function () { return [useEffectHook]; }); ; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(NoHooks, 'NoHooks', __FILENAME__); reactHotLoader.register(UseStateHook, 'UseStateHook', __FILENAME__); reactHotLoader.register(UseStateHooks, 'UseStateHooks', __FILENAME__); reactHotLoader.register(useEffectHook, 'useEffectHook', __FILENAME__); reactHotLoader.register(useForwardRefHook, 'useForwardRefHook', __FILENAME__); reactHotLoader.register(useForwardRefFunctionHook, 'useForwardRefFunctionHook', __FILENAME__); reactHotLoader.register(useCustomHook, 'useCustomHook', __FILENAME__); reactHotLoader.register(useCustomHookAgain, 'useCustomHookAgain', __FILENAME__); reactHotLoader.register(useInnerHook, 'useInnerHook', __FILENAME__); reactHotLoader.register(useFunc, 'useFunc', __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components issue 246.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.spread = spread; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; function spread() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return args.push(1); } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(spread, \\"spread\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components local hooks.js 1`] = ` "\\"use strict\\"; var _react = require(\\"react\\"); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; function Component1() { function useRippleHandler() {} useRippleHandler(); useRippleHandler(); } __signature__(Component1, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); function Component2() { var useRippleHandler = function useRippleHandler() {}; useRippleHandler(); useRippleHandler(); } __signature__(Component2, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); function Component3() { var useRippleHandler = function useRippleHandler() {}; useRippleHandler(); useRippleHandler(); } __signature__(Component3, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); var useInnerHook = function useInnerHook(_ref) { var useHookFromProps = _ref.useHookFromProps; var useHookBase = function useHookBase() { return (0, _react.useState)(); }; __signature__(useHookBase, \\"useState{}\\"); var useHook = function useHook() { return (0, _react.useState)(useHookFromProps(useHookBase())); }; __signature__(useHook, \\"useState{(useHookFromProps(useHookBase()))}\\\\nuseHookFromProps{}\\\\nuseHookBase{}\\", function () { return [useHookFromProps, useHookBase]; }); useHookFromProps(); { // sub scope useHook(); } }; __signature__(useInnerHook, \\"useHookFromProps{}\\\\nuseHook{}\\"); var OnlyThisOneUsesExternalHook = function OnlyThisOneUsesExternalHook() { useInnerHook(); (0, _react.useState)(); }; // check for \\"return [\\" __signature__(OnlyThisOneUsesExternalHook, \\"useInnerHook{}\\\\nuseState{}\\", function () { return [useInnerHook]; }); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Component1, \\"Component1\\", __FILENAME__); reactHotLoader.register(Component2, \\"Component2\\", __FILENAME__); reactHotLoader.register(Component3, \\"Component3\\", __FILENAME__); reactHotLoader.register(useInnerHook, \\"useInnerHook\\", __FILENAME__); reactHotLoader.register(OnlyThisOneUsesExternalHook, \\"OnlyThisOneUsesExternalHook\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "es2015" tags potential React components name clash.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; var _default = 10; var _default2 = 42; exports.default = _default2; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(_default, \\"_default\\", __FILENAME__); reactHotLoader.register(_default2, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods arguments.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { arguments; return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods arrow function in constructor.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.onClick = e => e.target.value; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods async functions expression body.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = async (a, b) => await b(a); } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods async functions.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = async (a, b) => { return await a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods block body.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods default params.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a = \\"foo\\") => { return \`\${a}bar\`; }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods destructured params.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = ({ a }, { b }) => { return \`\${a}\${b}\`; }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods expression body.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.onClick = e => e.target.value; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods nested arguments.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { () => { arguments; }; return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods nested new.target.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { () => { new.target; }; return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods new.target.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { new.target; return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods not a function.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = 2; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods not an arrow function.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = function (a, b) { return a(b); }; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods same name as class method.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo { constructor() { this.bar = (a, b) => { return a(b); }; } bar() { return 2; } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" copies arrow function body block onto hidden class methods static property.js 1`] = ` "\\"use strict\\"; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Foo {} Foo.bar = (a, b) => { return a(b); }; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Foo, \\"Foo\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components bindings.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.C = undefined; var _leftPad = require(\\"left-pad\\"); var _leftPad2 = _interopRequireDefault(_leftPad); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; const A = 42; function B() { function R() {} class S {} const T = 42; } class C { U() { function V() { class W {} } } // @ts-ignore __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } } exports.C = C; const D = class X {}; let E = D; var Y = require(\\"left-pad\\"); var { Z } = require(\\"left-pad\\"); const _default = React.createClass({ displayName: \\"_default\\" }); exports.default = _default; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(A, \\"A\\", __FILENAME__); reactHotLoader.register(B, \\"B\\", __FILENAME__); reactHotLoader.register(C, \\"C\\", __FILENAME__); reactHotLoader.register(D, \\"D\\", __FILENAME__); reactHotLoader.register(E, \\"E\\", __FILENAME__); reactHotLoader.register(_default, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components counter.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; class Counter {} const _default = () => React.createElement( \\"div\\", null, React.createElement(Counter, null) ); exports.default = _default; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Counter, \\"Counter\\", __FILENAME__); reactHotLoader.register(_default, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components drop hot half.prod.js 1`] = ` "'use strict'; var _reactHotLoader = require('react-hot-loader'); var _root = require('react-hot-loader/root'); const control = compose(withDebug, withDebug)(App); const targetCase1 = compose(withDebug, withDebug, (0, _reactHotLoader.hot)(module))(App); const targetCase2 = compose(withDebug, withDebug, _root.hot)(App); const removeHot1 = (0, _reactHotLoader.hot)(control); const removeHot2 = control; const removeHot3 = control;" `; exports[`babel Targetting "modern" tags potential React components drop hot.prod.js 1`] = ` "'use strict'; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.z = exports.e = exports.d = exports.c = exports.b = exports.a = undefined; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactHotLoader = require('react-hot-loader'); var RHL = _interopRequireWildcard(_reactHotLoader); var RHL2 = _interopRequireWildcard(_reactHotLoader); var _root = require('react-hot-loader/root'); var _notReactHotLoader = require('not-react-hot-loader'); var NOTRHL = _interopRequireWildcard(_notReactHotLoader); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const App = () => _react2.default.createElement( 'div', null, 'Hello World!' ); const a = App; const z = App; const b = App; const c = App; const d = App; const e = App; (0, _reactHotLoader.foo)(module)(App); (0, _notReactHotLoader.hot)(module)(App); (0, _reactHotLoader.foo)(module)(App); RHL.foo(module)(App); NOTRHL.hot(module)(App); // should not drop incomplete reference (0, _reactHotLoader.foo)(module); exports.a = a; exports.b = b; exports.c = c; exports.d = d; exports.e = e; exports.z = z;" `; exports[`babel Targetting "modern" tags potential React components hooks.js 1`] = ` "'use strict'; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _externalHook = require('external-hook'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; const NoHooks = () => _react2.default.createElement( 'div', null, 'no hooks' ); const UseStateHook = () => { (0, _react.useState)(42); }; __signature__(UseStateHook, 'useState{(42)}'); const UseStateHooks = () => { (0, _react.useState)(42); (0, _react.useState)(24); }; __signature__(UseStateHooks, 'useState{(42)}\\\\nuseState{(24)}'); const useEffectHook = () => { _react2.default.useEffect(() => ({})); (0, _react.useState)(24); _react2.default.useState(42); }; __signature__(useEffectHook, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}'); const useForwardRefHook = _react2.default.forwardRef(__signature__(() => { _react2.default.useEffect(() => ({})); (0, _react.useState)(24); _react2.default.useState(42); }, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}')); const useForwardRefFunctionHook = _react2.default.forwardRef(__signature__(function () { _react2.default.useEffect(() => ({})); (0, _react.useState)(24); _react2.default.useState(42); }, 'useEffect{}\\\\nuseState{(24)}\\\\nuseState{(42)}')); const useCustomHook = () => { (0, _react.useState)(42); useEffectHook(); (0, _externalHook.useExternalHook)(); }; __signature__(useCustomHook, 'useState{(42)}\\\\nuseEffectHook{}\\\\nuseExternalHook{}', () => [useEffectHook, _externalHook.useExternalHook]); const useCustomHookAgain = () => { (0, _react.useState)(42); useEffectHook(); (0, _externalHook.useExternalHook)(); }; __signature__(useCustomHookAgain, 'useState{(42)}\\\\nuseEffectHook{}\\\\nuseExternalHook{}', () => [useEffectHook, _externalHook.useExternalHook]); const useInnerHook = ({ useHookFromProps }) => { const useHookBase = () => (0, _react.useState)(); __signature__(useHookBase, 'useState{}'); const useHook = () => (0, _react.useState)(useHookFromProps(useHookBase())); __signature__(useHook, 'useState{(useHookFromProps(useHookBase()))}\\\\nuseHookFromProps{}\\\\nuseHookBase{}', () => [useHookFromProps, useHookBase]); useHookFromProps(); { // sub scope useHook(); } }; __signature__(useInnerHook, 'useHookFromProps{}\\\\nuseHook{}'); function useFunc() { (0, _react.useState)(42); useEffectHook(); } __signature__(useFunc, 'useState{(42)}\\\\nuseEffectHook{}', () => [useEffectHook]); ; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(NoHooks, 'NoHooks', __FILENAME__); reactHotLoader.register(UseStateHook, 'UseStateHook', __FILENAME__); reactHotLoader.register(UseStateHooks, 'UseStateHooks', __FILENAME__); reactHotLoader.register(useEffectHook, 'useEffectHook', __FILENAME__); reactHotLoader.register(useForwardRefHook, 'useForwardRefHook', __FILENAME__); reactHotLoader.register(useForwardRefFunctionHook, 'useForwardRefFunctionHook', __FILENAME__); reactHotLoader.register(useCustomHook, 'useCustomHook', __FILENAME__); reactHotLoader.register(useCustomHookAgain, 'useCustomHookAgain', __FILENAME__); reactHotLoader.register(useInnerHook, 'useInnerHook', __FILENAME__); reactHotLoader.register(useFunc, 'useFunc', __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components issue 246.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); exports.spread = spread; (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; function spread(...args) { return args.push(1); } ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(spread, \\"spread\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components local hooks.js 1`] = ` "\\"use strict\\"; var _react = require(\\"react\\"); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; function Component1() { function useRippleHandler() {} useRippleHandler(); useRippleHandler(); } __signature__(Component1, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); function Component2() { const useRippleHandler = () => {}; useRippleHandler(); useRippleHandler(); } __signature__(Component2, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); function Component3() { const useRippleHandler = function () {}; useRippleHandler(); useRippleHandler(); } __signature__(Component3, \\"useRippleHandler{}\\\\nuseRippleHandler{}\\"); const useInnerHook = ({ useHookFromProps }) => { const useHookBase = () => (0, _react.useState)(); __signature__(useHookBase, \\"useState{}\\"); const useHook = () => (0, _react.useState)(useHookFromProps(useHookBase())); __signature__(useHook, \\"useState{(useHookFromProps(useHookBase()))}\\\\nuseHookFromProps{}\\\\nuseHookBase{}\\", () => [useHookFromProps, useHookBase]); useHookFromProps(); { // sub scope useHook(); } }; __signature__(useInnerHook, \\"useHookFromProps{}\\\\nuseHook{}\\"); const OnlyThisOneUsesExternalHook = () => { useInnerHook(); (0, _react.useState)(); }; // check for \\"return [\\" __signature__(OnlyThisOneUsesExternalHook, \\"useInnerHook{}\\\\nuseState{}\\", () => [useInnerHook]); ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(Component1, \\"Component1\\", __FILENAME__); reactHotLoader.register(Component2, \\"Component2\\", __FILENAME__); reactHotLoader.register(Component3, \\"Component3\\", __FILENAME__); reactHotLoader.register(useInnerHook, \\"useInnerHook\\", __FILENAME__); reactHotLoader.register(OnlyThisOneUsesExternalHook, \\"OnlyThisOneUsesExternalHook\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; exports[`babel Targetting "modern" tags potential React components name clash.js 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); (function () { var enterModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.enterModule : undefined; enterModule && enterModule(module); })(); var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) { return a; }; const _default = 10; const _default2 = 42; exports.default = _default2; ; (function () { var reactHotLoader = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default : undefined; if (!reactHotLoader) { return; } reactHotLoader.register(_default, \\"_default\\", __FILENAME__); reactHotLoader.register(_default2, \\"default\\", __FILENAME__); })(); ; (function () { var leaveModule = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.leaveModule : undefined; leaveModule && leaveModule(module); })();" `; ================================================ FILE: test/babel.test.js ================================================ import path from 'path'; import fs from 'fs'; import { transformFileSync } from 'babel-core'; /* eslint-disable import/no-unresolved, import/extensions */ import { getOptions, TARGETS } from '../testConfig/babel'; import plugin from '../src/babel.dev'; /* eslint-enable import/no-unresolved, import/extensions */ const babelPlugin = path.resolve(__dirname, '../src/babel.dev'); const babelPluginProd = path.resolve(__dirname, '../src/babel.prod'); const FIXTURES_DIR = path.join(__dirname, '__babel_fixtures__'); function trim(str) { return str.replace(/^\s+|\s+$/, ''); } function addRHLPlugin(babel, prod = false) { babel.plugins.push(prod ? babelPluginProd : babelPlugin); return babel; } describe('babel', () => { TARGETS.forEach(target => { describe(`Targetting "${target}"`, () => { describe('tags potential React components', () => { fs.readdirSync(FIXTURES_DIR).forEach(fixtureName => { const fixtureFile = path.join(FIXTURES_DIR, fixtureName); const isForProd = fixtureName.endsWith('.prod.js'); if (fs.statSync(fixtureFile).isFile()) { it(fixtureName.split('-').join(' '), () => { const actual = transformFileSync(fixtureFile, addRHLPlugin(getOptions(target), isForProd)).code; const codeWithoutFilename = actual.replace( new RegExp(`["']${fixtureFile.replace(/\\/g, '/')}["']`, 'g'), '__FILENAME__', ); expect(trim(codeWithoutFilename)).toMatchSnapshot(); }); } }); }); describe('copies arrow function body block onto hidden class methods', () => { const fixturesDir = path.join(FIXTURES_DIR, 'class-properties'); fs.readdirSync(fixturesDir).forEach(fixtureName => { const fixtureFile = path.join(fixturesDir, fixtureName); if (fs.statSync(fixtureFile).isFile()) { it(fixtureName.split('-').join(' '), () => { const actual = transformFileSync(fixtureFile, addRHLPlugin(getOptions(target))).code; const codeWithoutFilename = actual.replace( new RegExp(`["']${fixtureFile.replace(/\\/g, '/')}["']`, 'g'), '__FILENAME__', ); expect(trim(codeWithoutFilename)).toMatchSnapshot(); }); } }); }); }); }); describe('babel helpers', () => { const { shouldIgnoreFile } = plugin; it('should ignore react and hot-loader', () => { expect(shouldIgnoreFile('node_modules/react')).toBe(true); expect(shouldIgnoreFile('node_modules\\react')).toBe(true); expect(shouldIgnoreFile('node_modules/react/xyz')).toBe(true); expect(shouldIgnoreFile('node_modules/react-hot-loader')).toBe(true); expect(shouldIgnoreFile('node_modules/react-hot-loader/xyz')).toBe(true); }); it('should pass all other files', () => { expect(shouldIgnoreFile('react')).toBe(false); expect(shouldIgnoreFile('node_modules/react-select')).toBe(false); expect(shouldIgnoreFile('node_modules\\react-select')).toBe(false); expect(shouldIgnoreFile('some_modules/react/xyz')).toBe(false); expect(shouldIgnoreFile('node_modules/react-cold-loader')).toBe(false); expect(shouldIgnoreFile('react-hot-loader.js')).toBe(false); }); }); }); ================================================ FILE: test/build.test.js ================================================ /* eslint-disable camelcase */ import { REGENERATE_METHOD as REGENERATE_METHOD_std } from '../src/proxy'; import { REGENERATE_METHOD as REGENERATE_METHOD_rhl } from '../src/internal/constants'; describe('Consistency tests', () => { it('Should be constant con', () => { expect(REGENERATE_METHOD_rhl).toBe(REGENERATE_METHOD_std); }); }); ================================================ FILE: test/cases/hooks/useContext.test.js ================================================ /* eslint-env browser */ /* eslint-disable no-lone-blocks, global-require */ import 'babel-polyfill'; import React from 'react'; import TestRenderer from 'react-test-renderer'; import ReactHotLoader, { AppContainer, setConfig } from '../../../src/index.dev'; import { configureGeneration } from '../../../src/global/generation'; // jest.mock('react-dom', () => require('@hot-loader/react-dom')); describe(`Hooks: useContext`, () => { beforeEach(() => { ReactHotLoader.reset(); setConfig({ ignoreSFC: true, }); configureGeneration(1, 1); }); if (React.useContext) { it('use', () => { let failed = false; const context = React.createContext(0); const Wrapper = () => { const ctx = React.useContext(context); if (ctx) { return `pass ${ctx}`; } failed = true; return 'fail'; }; ReactHotLoader.register(Wrapper, 'wrapper', 'hook-test'); const wrapper = TestRenderer.create( , ); expect(wrapper.toJSON()).toEqual('pass 1'); expect(failed).toBe(false); { ReactHotLoader.register(Wrapper, 'wrapper', 'hook-test'); wrapper.update( , ); expect(wrapper.toJSON()).toEqual('pass 2'); expect(failed).toBe(false); } }); } else { it('target platform does not support useContext', () => { expect(true).toBe(true); }); } }); ================================================ FILE: test/cases/memo/memo.test.js ================================================ /* eslint-env browser */ import 'babel-polyfill'; import React from 'react'; import TestRenderer from 'react-test-renderer'; import ReactHotLoader, { AppContainer } from '../../../src/index.dev'; import { configureGeneration } from '../../../src/global/generation'; // jest.mock('react-dom', () => require('@hot-loader/react-dom')); describe(`React.memo`, () => { beforeEach(() => { ReactHotLoader.reset(); configureGeneration(1, 1); }); const snapShot = { children: ['this is component 2'], props: {}, type: 'div', }; if (React.memo) { it('memo wrapping functional component', () => { const Memo = React.memo(() =>
    this is component 1
    ); ReactHotLoader.register(Memo, 'memo', 'memo-test'); const wrapper = TestRenderer.create( , ); { const Memo = React.memo(() =>
    this is component 2
    ); ReactHotLoader.register(Memo, 'memo', 'memo-test'); wrapper.update( , ); expect(wrapper.toJSON()).toEqual(snapShot); } }); it('memo wrapping forwardRef component', () => { const Memo = React.memo(React.forwardRef(() =>
    this is component 1
    )); ReactHotLoader.register(Memo, 'memo', 'memo-test'); const wrapper = TestRenderer.create( , ); { const Memo = React.memo(React.forwardRef(() =>
    this is component 2
    )); ReactHotLoader.register(Memo, 'memo', 'memo-test'); wrapper.update( , ); expect(wrapper.toJSON()).toEqual(snapShot); } }); } else { it('target platform does not support React.memo', () => { expect(true).toBe(true); }); } }); ================================================ FILE: test/global/generation.test.js ================================================ import * as generation from '../../src/global/generation'; describe('generation', () => { it('should maintain generation counter', () => { expect(generation.get()).toBe(1); generation.increment(); expect(generation.get()).toBe(2); }); }); ================================================ FILE: test/global/modules.test.js ================================================ import * as modules from '../../src/global/modules'; describe('modules', () => { it('should register enter/leave module', () => { const module = { id: 'myModule.js' }; expect(modules.isOpened(module)).toBe(false); modules.enter(module); expect(modules.isOpened(module)).toBe(true); modules.leave(module); expect(modules.isOpened(module)).toBe(false); }); }); ================================================ FILE: test/hot/.gitignore ================================================ react-dom ================================================ FILE: test/hot/createPatchedReact.js ================================================ const { writeFileSync, readFileSync, readdirSync, statSync, mkdirSync } = require('fs'); const { default: { patch }, } = require('../../webpack'); const copyFile = (source, dest) => { const data = readFileSync(source).toString(); const patchedData = patch(data); writeFileSync(dest, patchedData || data); }; const copy = (source, target) => { const list = readdirSync(source); for (const i of list) { const sourceFile = `${source}/${i}`; const targetFile = `${target}/${i}`; const stat = statSync(sourceFile); if (stat.isDirectory()) { mkdirSync(`${target}/${i}`); copy(sourceFile, targetFile); } else { copyFile(sourceFile, targetFile); } } }; const TARGET = `${__dirname}/react-dom`; mkdirSync(TARGET); copy('./node_modules/react-dom/', TARGET); ================================================ FILE: test/hot/react-dom.integration.spec.js ================================================ /* eslint-env browser */ /* eslint-disable no-lone-blocks, global-require, prefer-template, import/no-unresolved */ import 'babel-polyfill'; import React, { Suspense } from 'react'; import ReactDom from 'react-dom'; // import TestRenderer from 'react-test-renderer'; import ReactHotLoader, { setConfig } from '../../src/index.dev'; import { configureGeneration, enterHotUpdate, incrementHotGeneration } from '../../src/global/generation'; import configuration from '../../src/configuration'; import AppContainer from '../../src/AppContainer.dev'; import reactHotLoader from '../../src/reactHotLoader'; import reconcileHotReplacement from '../../src/reconciler'; jest.mock('react-dom', () => { const reactDom = require('./react-dom'); return reactDom; }); describe(`🔥-dom`, () => { beforeEach(() => { ReactHotLoader.reset(); setConfig({ ignoreSFC: true, }); configureGeneration(1, 1); reactHotLoader.register(AppContainer, 'AppContainer', 'test'); }); const tick = () => new Promise(resolve => setTimeout(resolve, 10)); if (React.useContext && String(() => 42).indexOf('=>') > 0) { it('shall integrate with React', () => { expect(configuration.IS_REACT_MERGE_ENABLED).toBe(true); expect(configuration.integratedResolver).toBe(true); expect(React.useEffect.isPatchedByReactHotLoader).toBe(true); }); it('should use component comparator', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect(() => { mount('test1'); return unmount; }, []); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun1 = () => { React.useEffect(() => { mount('test2'); return unmount; }, []); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); // THIS TEST IS EXPECTED TO FAIL ON LOCAL MACHINE (have no idea why)!! expect(mount).not.toHaveBeenCalledWith('test2'); expect(unmount).not.toHaveBeenCalled(); }); it('should fail component comparator', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect(() => { mount('test1'); return unmount; }, []); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun2 = () => { React.useEffect(() => { mount('test2'); return unmount; }, []); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); expect(mount).toHaveBeenCalledWith('test2'); expect(unmount).toHaveBeenCalled(); }); it('should reload context', async () => { const mount = jest.fn(); const unmount = jest.fn(); const genApp = contextValue => { const context = React.createContext(contextValue); const RenderContext = () => { const v = React.useContext(context); return ( contextValue={v} {v => `~${v}~`} ); }; const MountCheck = () => { React.useEffect(() => { mount('test1'); return unmount; }, []); return 'fun1'; }; const App = () => (
    ); ReactHotLoader.register(context, 'context', 'test'); return App; }; const el = document.createElement('div'); const App1 = genApp('1-test-1'); ReactDom.render(, el); expect(el.innerHTML).toMatch(/1-test-1/); expect(el.innerHTML).toMatch(/~1-test-1~/); expect(el.innerHTML).toMatch(/~~1-test-1~~/); incrementHotGeneration(); { const App1 = genApp('2-hot-2'); ReactDom.render(, el); } await tick(); expect(el.innerHTML).toMatch(/2-hot-2/); expect(el.innerHTML).toMatch(/~2-hot-2~/); expect(el.innerHTML).toMatch(/~~2-hot-2~~/); expect(mount).toHaveBeenCalledTimes(1); expect(unmount).toHaveBeenCalledTimes(0); }); it('should reload hook effect', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect( () => { mount('test1'); return unmount; }, ['hot'], ); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun1 = () => { React.useEffect( () => { mount('test2'); return unmount; }, ['hot'], ); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); expect(unmount).toHaveBeenCalled(); expect(mount).toHaveBeenCalledWith('test2'); }); it('should fail on hook order change', async () => { const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test1'), []); return state; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test0'); incrementHotGeneration(); { const Fun1 = () => { React.useState('anotherstate'); const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test1'), []); return state; }; expect(() => ReactDom.render(, el)).toThrow(); } }); it('should set on hook order change if signature provided', async () => { const ref = React.createRef(); const App = ({ children }) => ( {children} ); const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test1'), []); return state; }; const Fun2 = () => { const [state, setState] = React.useState('step1'); React.useEffect(() => setState('step2'), []); return state; }; reactHotLoader.signature(Fun1, 'fun1-key1'); reactHotLoader.register(Fun1, 'Fun1', 'test'); const el = document.createElement('div'); ReactDom.render( , el, ); expect(el.innerHTML).toEqual('test0step1'); await tick(); expect(el.innerHTML).toEqual('test1step2'); { const Fun1 = () => { React.useState('anotherstate'); const [state, setState] = React.useState('test-new'); React.useEffect(() => setState('test1'), []); return state; }; reactHotLoader.signature(Fun1, 'fun1-key2'); reactHotLoader.register(Fun1, 'Fun1', 'test'); incrementHotGeneration(); enterHotUpdate(); reconcileHotReplacement(ref.current); expect(() => ReactDom.render( , el, ), ).not.toThrow(); expect(el.innerHTML).toEqual('test-newstep2'); } }); it('should reset hook comparator', async () => { const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test1'), []); return state; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test0'); await tick(); expect(el.innerHTML).toEqual('test1'); incrementHotGeneration(); { const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test2'), []); return state; }; ReactDom.render(, el); } await tick(); // THIS TEST IS EXPECTED TO FAIL ON LOCAL MACHINE (have no idea why)!! expect(el.innerHTML).toEqual('test1'); incrementHotGeneration(); { const f = () => React.useState(0); // aka fail const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test3'), []); f(); return 'h' + state; }; ReactHotLoader.signature(Fun1, 'somevalue'); ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('htest3'); }); it('should support classes', async () => { class Comp1 extends React.Component { state = { x: 1, }; render() { return 'test1' + this.state.x; } } Comp1.displayName = 'Comp1'; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test11'); if (configuration.intergratedResolver) { expect(.type).toBe(Comp1); } incrementHotGeneration(); { class Comp1 extends React.Component { state = { x: 2, }; render() { return 'test2' + this.state.x; } } Comp1.displayName = 'Comp1'; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('test21'); }); it('should support registered classes', async () => { class Comp1 extends React.Component { state = { x: 1, }; render() { return 'test1' + this.state.x; } } ReactHotLoader.register(Comp1, 'comp1', 'test'); const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test11'); if (configuration.intergratedResolver) { expect(.type).toBe(Comp1); } incrementHotGeneration(); { class Comp1 extends React.Component { state = { x: 2, }; render() { return 'test2' + this.state.x; } } ReactHotLoader.register(Comp1, 'comp1', 'test'); ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('test21'); }); it('support lazy memo forward in Provider', () => { setConfig({ wrapLazy: false, }); const spy = jest.fn(); const sandbox = x => { const Comp = () => { React.useEffect(() => () => spy(), []); return
    lazy {x}
    ; }; ReactHotLoader.register(Comp, 'S1Comp', 'test'); // use sync importer const importer = { then(x) { const result = x({ default: Comp }); return { then(cb) { cb(result); }, }; }, }; const Lazy = React.lazy(() => importer); ReactHotLoader.register(Lazy, 'S1Lazy', 'test'); const Context = React.createContext(); ReactHotLoader.register(Context, 'S1Context', 'test'); const Forward = React.forwardRef(() => ( )); const Memo = Forward; // React.memo(Forward); ReactHotLoader.register(Memo, 'S1Memo', 'test'); return () => ( ); }; const S1 = sandbox(1); ReactHotLoader.register(S1, 'S1', 'test'); const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('
    lazy 1
    '); expect(spy).not.toHaveBeenCalled(); incrementHotGeneration(); const S2 = sandbox(2); ReactHotLoader.register(S2, 'S1', 'test'); ReactDom.render(, el); expect(el.innerHTML).toEqual('
    lazy 2
    '); expect(spy).not.toHaveBeenCalled(); }); } else { it('target platform does not support useContext', () => { expect(true).toBe(true); }); } }); ================================================ FILE: test/hot/react-dom.no-integration.spec.js ================================================ /* eslint-env browser */ /* eslint-disable no-lone-blocks, global-require, prefer-template, import/no-unresolved */ import 'babel-polyfill'; import React, { Suspense } from 'react'; import ReactDom from 'react-dom'; // import TestRenderer from 'react-test-renderer'; import ReactHotLoader, { setConfig } from '../../src/index.dev'; import { configureGeneration, incrementHotGeneration } from '../../src/global/generation'; import configuration from '../../src/configuration'; import { AppContainer } from '../../index'; describe(`🔥-dom`, () => { beforeEach(() => { ReactHotLoader.reset(); setConfig({ ignoreSFC: true, }); configureGeneration(1, 1); }); const tick = () => new Promise(resolve => setTimeout(resolve, 10)); if (React.useContext && String(() => 42).indexOf('=>') > 0) { it('shall (not) integrate with React', () => { expect(configuration.IS_REACT_MERGE_ENABLED).toBe(false); expect(configuration.integratedResolver).toBe(false); expect(React.useEffect.isPatchedByReactHotLoader).toBe(true); }); it('should (not) use component comparator', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect(() => { mount('test1'); return unmount; }, []); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun1 = () => { React.useEffect(() => { mount('test2'); return unmount; }, []); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); expect(mount).toHaveBeenCalledWith('test2'); expect(unmount).toHaveBeenCalled(); }); it('should fail component comparator', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect(() => { mount('test1'); return unmount; }, []); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun2 = () => { React.useEffect(() => { mount('test2'); return unmount; }, []); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); expect(mount).toHaveBeenCalledWith('test2'); expect(unmount).toHaveBeenCalled(); }); it('should reload hook effect', async () => { const mount = jest.fn(); const unmount = jest.fn(); const Fun1 = () => { React.useEffect( () => { mount('test1'); return unmount; }, ['hot'], ); return 'fun1'; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('fun1'); incrementHotGeneration(); { const Fun1 = () => { React.useEffect( () => { mount('test2'); return unmount; }, ['hot'], ); return 'fun2'; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('fun2'); expect(mount).toHaveBeenCalledWith('test1'); expect(unmount).toHaveBeenCalled(); expect(mount).toHaveBeenCalledWith('test2'); }); it('should reset hook comparator', async () => { const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test1'), []); return state; }; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test0'); await tick(); expect(el.innerHTML).toEqual('test1'); incrementHotGeneration(); { const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test2'), []); return state; }; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('test2'); incrementHotGeneration(); { const f = () => React.useState(0); // aka fail const Fun1 = () => { const [state, setState] = React.useState('test0'); React.useEffect(() => setState('test3'), []); f(); return 'h' + state; }; ReactHotLoader.signature(Fun1, 'somevalue'); ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('htest3'); }); it('should support classes', async () => { class Comp1 extends React.Component { state = { x: 1, }; render() { return 'test1' + this.state.x; } } Comp1.displayName = 'Comp1'; const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test11'); if (configuration.intergratedResolver) { expect(.type).toBe(Comp1); } incrementHotGeneration(); { class Comp1 extends React.Component { state = { x: 2, }; render() { return 'test2' + this.state.x; } } Comp1.displayName = 'Comp1'; ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('test22'); }); it('should support registered classes', async () => { class Comp1 extends React.Component { state = { x: 1, }; render() { return 'test1' + this.state.x; } } ReactHotLoader.register(Comp1, 'comp1', 'test'); const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('test11'); if (configuration.intergratedResolver) { expect(.type).toBe(Comp1); } incrementHotGeneration(); { class Comp1 extends React.Component { state = { x: 2, }; render() { return 'test2' + this.state.x; } } ReactHotLoader.register(Comp1, 'comp1', 'test'); ReactDom.render(, el); } await tick(); expect(el.innerHTML).toEqual('test21'); }); it('support lazy memo forward', () => { setConfig({ wrapLazy: false, }); const spy = jest.fn(); const sandbox = x => { const Comp = () => { React.useEffect(() => () => spy(), []); return
    lazy {x}
    ; }; ReactHotLoader.register(Comp, 'S1Comp', 'test'); // use sync importer const importer = { then(x) { const result = x({ default: Comp }); return { then(cb) { cb(result); }, }; }, }; const Lazy = React.lazy(() => importer); ReactHotLoader.register(Lazy, 'S1Lazy', 'test'); const Context = React.createContext(); ReactHotLoader.register(Context, 'S1Context', 'test'); const Forward = React.forwardRef(() => ( )); const Memo = Forward; // React.memo(Forward); ReactHotLoader.register(Memo, 'S1Memo', 'test'); return () => ( ); }; const S1 = sandbox(1); ReactHotLoader.register(S1, 'S1', 'test'); const el = document.createElement('div'); ReactDom.render(, el); expect(el.innerHTML).toEqual('
    lazy 1
    '); expect(spy).not.toHaveBeenCalled(); incrementHotGeneration(); const S2 = sandbox(2); ReactHotLoader.register(S2, 'S1', 'test'); ReactDom.render(, el); expect(el.innerHTML).toEqual('
    lazy 2
    '); expect(spy).toHaveBeenCalled(); }); } else { it('target platform does not support useContext', () => { expect(true).toBe(true); }); } }); ================================================ FILE: test/hot.dev.test.js ================================================ import React, { Component } from 'react'; import { mount } from 'enzyme'; import { enter as enterModule, leave as leaveModule, isOpened, hotModule } from '../src/global/modules'; import '../src/index.dev'; import hot from '../src/hot.dev'; import logger from '../src/logger'; import configuration from '../src/configuration'; import { increment as incrementGeneration } from '../src/global/generation'; jest.mock('../src/logger'); describe('hot (dev)', () => { it('should attach to the general hot API', () => { const sourceModule = { id: 42, hot: { accept: jest.fn(), }, }; hot(sourceModule); expect(sourceModule.hot.accept).toBeCalled(); }); it('should attach to webpack hot API', () => { const sourceModule = { id: 42, hot: { accept: jest.fn(), status: () => 'idle', addStatusHandler: jest.fn(), }, }; hot(sourceModule); expect(hotModule(sourceModule.id).instances.length).toBe(0); expect(sourceModule.hot.accept).toHaveBeenCalledTimes(1); expect(sourceModule.hot.addStatusHandler).toHaveBeenCalledTimes(1); }); it('should not attach on HRM event', () => { const sourceModule = { id: 42, hot: { accept: jest.fn(), status: () => 'not-idle', addStatusHandler: jest.fn(), }, }; hot(sourceModule); expect(sourceModule.hot.accept).toHaveBeenCalledTimes(1); expect(sourceModule.hot.addStatusHandler).not.toBeCalled(); }); it('should stand mount/unmount in normal situation', () => { const sourceModule = { id: 42, hot: { accept: jest.fn(), }, }; const Component = () =>
    123
    ; const HotComponent = hot(sourceModule)(Component); const wrapper = mount(); wrapper.unmount(); }); // this test is very flaky it.skip('should redraw component on HRM', done => { const callbacks = []; const sourceModule = { id: 'error42', hot: { accept(callback) { callbacks.push(callback); }, }, }; const spy = jest.fn(); enterModule(sourceModule); class MyComponent extends Component { render() { spy(); return
    42
    ; } } const HotComponent = hot(sourceModule)(MyComponent); hot(sourceModule)(MyComponent); mount(); expect(spy).toHaveBeenCalledTimes(1); expect(callbacks.length).toBe(2); incrementGeneration(); callbacks.forEach(cb => cb()); expect(spy).toHaveBeenCalledTimes(1); setTimeout(() => { setTimeout(() => { expect(spy).toHaveBeenCalledTimes(configuration.pureRender ? 4 : 3); done(); }, 1); }, 1); }); it('should trigger error in unmount in opened state', () => { const sourceModule = { id: 'error42_unmount', hot: { accept: jest.fn(), }, }; logger.error.mockClear(); enterModule(sourceModule); const Component = () =>
    123
    ; const HotComponent = hot(sourceModule)(Component); expect(hotModule(sourceModule.id).instances.length).toBe(0); const wrapper1 = mount(); const wrapper2 = mount(); expect(hotModule(sourceModule.id).instances.length).toBe(2); wrapper1.unmount(); expect(hotModule(sourceModule.id).instances.length).toBe(1); expect(logger.error).toHaveBeenCalledTimes(1); expect(logger.error) .toHaveBeenCalledWith(`React-hot-loader: Detected AppContainer unmount on module 'error42_unmount' update. Did you use "hot(Component)" and "ReactDOM.render()" in the same file? "hot(Component)" shall only be used as export. Please refer to "Getting Started" (https://github.com/gaearon/react-hot-loader/).`); wrapper2.unmount(); expect(hotModule(sourceModule.id).instances.length).toBe(0); }); it('it should track module state', () => { const sourceModule = { id: 'module42' }; expect(isOpened(sourceModule)).toBe(false); enterModule(sourceModule); expect(isOpened(sourceModule)).toBe(true); enterModule(sourceModule); expect(isOpened(sourceModule)).toBe(true); // leaveModule(sourceModule); expect(isOpened(sourceModule)).toBe(false); leaveModule(sourceModule); expect(isOpened(sourceModule)).toBe(false); }); it('it should stand wrong module definition', () => { const sourceModule = {}; expect(isOpened(sourceModule)).toBe(false); enterModule(sourceModule); expect(isOpened(sourceModule)).toBe(false); leaveModule(sourceModule); expect(isOpened(sourceModule)).toBe(false); enterModule(); isOpened(); leaveModule(); }); }); ================================================ FILE: test/index.dev.test.js ================================================ import React from 'react'; import * as indexExport from '../src/index.dev'; describe('patch (dev)', () => { it('should export reactHotLoader', () => { expect(typeof indexExport.hot).toBe('function'); expect(typeof indexExport.AppContainer).toBe('function'); }); it('should patch React methods', () => { expect(React.createElement.isPatchedByReactHotLoader).toBe(true); expect(React.createFactory.isPatchedByReactHotLoader).toBe(true); expect(React.Children.only.isPatchedByReactHotLoader).toBe(true); }); }); ================================================ FILE: test/internal/getReactStack.test.js ================================================ import React from 'react'; import { mount } from 'enzyme'; import getReactStack from '../../src/internal/getReactStack'; const getInstanceOf = element => { let instance; class Root extends React.Component { render() { instance = this; return element; } } mount(); return { instance, Root }; }; describe('getReactStack', () => { it('should generate React stack from instance', () => { const { instance, Root } = getInstanceOf(
    ); const stack = getReactStack(instance); expect(stack.type).toBe(Root); expect(stack.instance).toBeInstanceOf(Root); expect(stack.children).toHaveLength(1); expect(stack.children[0].type).toBe('div'); expect(stack.children[0].instance).toBeDefined(); expect(stack.children[0].children).toHaveLength(0); }); it('should handle components', () => { const Div = () =>
    ; const { instance, Root } = getInstanceOf(
    ); const stack = getReactStack(instance); expect(stack.type).toBe(Root); expect(stack.instance).toBeInstanceOf(Root); expect(stack.children).toHaveLength(1); expect(stack.children[0].type).toBe(Div); expect(stack.children[0].instance).toBeDefined(); expect(stack.children[0].children).toHaveLength(1); expect(stack.children[0].children[0].type).toBe('div'); expect(stack.children[0].children[0].instance).toBeDefined(); expect(stack.children[0].children[0].children).toHaveLength(0); }); if (React.version.startsWith('16')) { it('should handle multiple children (only Fiber)', () => { const Div = () =>
    ; const { instance, Root } = getInstanceOf([
    ,
    ]); const stack = getReactStack(instance); expect(stack.type).toBe(Root); expect(stack.instance).toBeInstanceOf(Root); expect(stack.children).toHaveLength(2); function expectToBeDivStack(child) { expect(child.type).toBe(Div); expect(child.instance.SFC_fake).toBe(Div); expect(child.children).toHaveLength(1); expect(child.children[0].type).toBe('div'); expect(child.children[0].instance).toBeDefined(); expect(child.children[0].children).toHaveLength(0); } expectToBeDivStack(stack.children[0]); expectToBeDivStack(stack.children[1]); }); } it('should handle complex structure', () => { class A extends React.Component { render() { return
    ; } } class B extends React.Component { render() { return ; } } const { instance, Root } = getInstanceOf( , ); const stack = getReactStack(instance); expect(stack.type).toBe(Root); expect(stack.instance).toBeInstanceOf(Root); expect(stack.children).toHaveLength(1); expect(stack.children[0].type).toBe('div'); expect(stack.children[0].children).toHaveLength(2); function expectToBeAStack(child) { expect(child.type).toBe(A); expect(child.instance).toBeInstanceOf(A); expect(child.children).toHaveLength(1); expect(child.children[0].type).toBe('div'); expect(child.children[0].instance).toBeDefined(); expect(child.children[0].children).toHaveLength(0); } function expectToBeBStack(child) { expect(child.type).toBe(B); expect(child.instance).toBeInstanceOf(B); expect(child.children).toHaveLength(1); expectToBeAStack(child.children[0]); } expectToBeAStack(stack.children[0].children[0]); expectToBeBStack(stack.children[0].children[1]); }); }); ================================================ FILE: test/internal/reactUtils.test.js ================================================ import React from 'react'; import { mount } from 'enzyme'; import { isCompositeComponent, getComponentDisplayName, getInternalInstance, updateInstance, isReactClass, isReactClassInstance, } from '../../src/internal/reactUtils'; describe('reactUtils', () => { describe('isReact', () => { it('isReactClass', () => { class C1 extends React.Component {} class C2 extends C1 {} const F1 = () => 42; expect(isReactClass(F1)).toBe(false); expect(isReactClass(C1)).toBe(true); expect(isReactClass(C2)).toBe(true); }); it('isReactClassInstance', () => { class C1 extends React.Component {} class C2 extends C1 {} const F1 = function F1() {}; expect(isReactClassInstance(new F1())).toBe(false); expect(isReactClassInstance(new C1())).toBe(true); expect(isReactClassInstance(new C2())).toBe(true); }); }); describe('#isCompositeComponent', () => { it('should return true if this is a composite component', () => { const FunctionalComponent = () => null; class ClassComponent extends React.Component { render() { return null; } } expect(isCompositeComponent('div')).toBe(false); expect(isCompositeComponent(ClassComponent)).toBe(true); expect(isCompositeComponent(FunctionalComponent)).toBe(true); }); }); describe('#getComponentDisplayName', () => { it('should return displayName if specified', () => { const ComponentWithDisplayName = () => null; ComponentWithDisplayName.displayName = 'CustomDisplayName'; expect(getComponentDisplayName(ComponentWithDisplayName)).toBe('CustomDisplayName'); }); it('should return name either', () => { const ComponentWithoutDisplayName = () => null; expect(getComponentDisplayName(ComponentWithoutDisplayName)).toBe('ComponentWithoutDisplayName'); }); it('should return "Component" either', () => { expect(getComponentDisplayName('div')).toBe('Component'); }); }); describe('#getInternalInstance', () => { it('should return internal instance', () => { let instance; class Component extends React.Component { render() { instance = this; return null; } } mount(); if (React.version === '16') { expect(getInternalInstance(instance).constructor.name).toBe('FiberNode'); } else if (React.version === '15') { expect(getInternalInstance(instance).constructor.name).toBe('ReactCompositeComponentWrapper'); } }); }); describe('#updateInstance', () => { it('should call forceUpdate', () => { let instance; class Component extends React.Component { render() { instance = this; return null; } } mount(); instance.forceUpdate = jest.fn(); updateInstance(instance); expect(instance.forceUpdate).toHaveBeenCalled(); }); }); }); ================================================ FILE: test/prod/AppContainer.prod.test.js ================================================ import React from 'react'; import { shallow } from 'enzyme'; import { AppContainer } from '../../src/index.prod'; describe('AppContainer (prod)', () => { it('should render child', () => { const App = () =>
    Hello world!
    ; const wrapper = shallow( , ); expect(wrapper.equals()).toBe(true); }); it('should throw an error with several children', () => { const App = () =>
    Hello world!
    ; expect(() => { shallow( , ); }).toThrow(); }); }); ================================================ FILE: test/prod/hot.prod.test.js ================================================ import React from 'react'; import { hot } from '../../src/index.prod'; describe('hot (prod)', () => { it('should be an identity', () => { const App = () =>
    Hello world!
    ; const HotApp = hot()(App); expect(App).toBe(HotApp); }); }); ================================================ FILE: test/prod/utils.prod.test.js ================================================ import React from 'react'; import { areComponentsEqual, setConfig } from '../../src/index.prod'; describe('utils (prod)', () => { describe('#areComponentsEqual', () => { it('should test if two components are equal', () => { const Comp1 = () =>
    ; const Comp2 = () =>
    ; expect(areComponentsEqual(Comp1, Comp1)).toBe(true); expect(areComponentsEqual(Comp1, Comp2)).toBe(false); }); }); describe('#setConfig', () => { it('should be a function', () => { expect(setConfig).toEqual(expect.any(Function)); }); }); }); ================================================ FILE: test/proxy/consistency.test.js ================================================ /* eslint-env jest */ /* eslint-disable react/no-render-return-value */ import React from 'react'; import { createMounter, ensureNoWarnings } from './helper'; import createProxy from '../../src/proxy'; import configuration from '../../src/configuration'; import '../../src/index.dev'; import { configureGeneration } from '../../src/global/generation'; const createFixtures = () => ({ modern: { Bar: class Bar extends React.Component { componentWillUnmount() { this.didUnmount = true; } doNothing() {} /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    Bar
    ; } }, Baz: class Baz extends React.Component { componentWillUnmount() { this.didUnmount = true; } thisIsES6 = () => {}; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    Baz
    ; } }, Foo: class Foo extends React.Component { static displayName = 'Foo (Custom)'; componentWillUnmount() { this.didUnmount = true; } /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    Foo
    ; } }, }, }); describe('consistency', () => { beforeEach(() => { configureGeneration(1, 1); }); ensureNoWarnings(); const { mount } = createMounter(); Object.keys(createFixtures()).forEach(type => { describe(type, () => { let Bar; let Baz; let Foo; beforeEach(() => { ({ Bar, Baz, Foo } = createFixtures()[type]); }); it('overwrites the original class', () => { // spin up const proxy = createProxy(Bar); const Proxy = proxy.get(); const barWrapper = mount(); const barInstance = barWrapper.instance(); expect(barWrapper.text()).toBe('Bar'); // replace base component proxy.update(Baz); const realBarWrapper = mount(); const realBarInstance = realBarWrapper.instance(); expect(realBarWrapper.text()).toBe('Baz'); expect(barInstance).toBe(realBarInstance); expect(barInstance.didUnmount).not.toBe(true); }); it('returns an existing proxy when wrapped twice', () => { const proxy = createProxy(Bar); const Proxy = proxy.get(); const proxyTwice = createProxy(Proxy); expect(proxyTwice).toBe(proxy); }); /* * https://github.com/reactjs/react-redux/issues/163#issuecomment-192556637 */ it('avoid false positives when statics are hoisted', () => { const fooProxy = createProxy(Foo); const FooProxy = fooProxy.get(); class Stuff extends React.Component { render() { return null; } } const KNOWN_STATICS = { name: true, length: true, prototype: true, caller: true, arguments: true, arity: true, type: true, }; Object.getOwnPropertyNames(FooProxy).forEach(key => { if (!KNOWN_STATICS[key]) { Stuff[key] = FooProxy[key]; } }); const stuffProxy = createProxy(Stuff); expect(stuffProxy).not.toBe(fooProxy); }); it('prevents recursive proxy cycle', () => { const proxy = createProxy(Bar); const Proxy = proxy.get(); proxy.update(Proxy); expect(proxy.get()).toBe(Proxy); }); it('prevents double proxy creation', () => { const proxy1 = createProxy(Bar); const proxy2 = createProxy(Bar); expect(proxy1.get()).toBe(proxy2.get()); }); it('prevents mutually recursive proxy cycle', () => { const barProxy = createProxy(Bar); const BarProxy = barProxy.get(); const fooProxy = createProxy(Foo); const FooProxy = fooProxy.get(); barProxy.update(FooProxy); fooProxy.update(BarProxy); }); it('sets up constructor to match the type', () => { const proxy = createProxy(Bar); const BarProxy = proxy.get(); const barInstance = mount().instance(); expect(barInstance.constructor).toBe(BarProxy); expect(barInstance instanceof BarProxy).toBe(true); proxy.update(Baz); const BazProxy = proxy.get(); expect(BarProxy).toBe(BazProxy); expect(barInstance.constructor).toBe(BazProxy); expect(barInstance instanceof BazProxy).toBe(true); }); it('replaces toString (if readable)', () => { const barProxy = createProxy(Bar, 'bar'); const BarProxy = barProxy.get(); expect(BarProxy.toString()).toMatch(/^(class|function) Bar/); Object.defineProperty(Foo, 'toString', { configurable: false, value: Foo.toString, writable: false, }); createProxy(Foo, 'foo'); }); it('sets up displayName from displayName or name', () => { const proxy = createProxy(Bar); const Proxy = proxy.get(); const barInstance = mount().instance(); expect(barInstance.constructor.displayName).toBe('Bar'); proxy.update(Baz); expect(barInstance.constructor.displayName).toBe('Baz'); proxy.update(Foo); expect(barInstance.constructor.displayName).toBe('Foo (Custom)'); }); it('inherits from base', () => { const proxy = createProxy(Bar); const Proxy = proxy.get(); expect(Proxy.prototype instanceof Bar).toBe(true); }); it('should transparently pass arguments to the render function', () => { const spy = jest.fn(); class Foo extends React.Component { render(...args) { spy(...args); return null; } } const proxy = createProxy(Foo); const Proxy = proxy.get(); const instance = new Proxy(); const props = {}; const state = {}; instance.render(props, state); expect(spy).toHaveBeenCalledWith(props, state); instance.render(1, 2); expect(spy).toHaveBeenCalledWith(1, 2); instance.render(0); expect(spy).toHaveBeenCalledWith(0); }); it('should revert arrow member change', () => { /* eslint-disable */ class BaseClass extends React.Component { arrow = () => 42; render() { return this.arrow(); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } } class Update1Class extends React.Component { arrow = () => 43; render() { return this.arrow(); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } } class Update2Class extends React.Component { arrow = () => 42; render() { return this.arrow(); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } } const proxy = createProxy(BaseClass); const Proxy = proxy.get(); const instance = new Proxy(); expect(instance.render()).toBe(42); proxy.update(Update1Class); new Proxy(); // side effect //closeGeneration(); expect(instance.render()).toBe(43); proxy.update(Update2Class); new Proxy(); // side effect //closeGeneration(); expect(instance.render()).toBe(42); /* eslint-enable */ }); it('should reflect external dependencies(broken, regression)', () => { /* eslint-disable */ const externalValue = 42; let gen = 0; const generator2 = () => { const g = gen++; return () => g; }; const generator3 = () => { const g = gen++; return () => g; }; class BaseClass extends React.Component { secret1 = 1; secret2 = generator2(); secret3 = generator3(); arrow1 = () => externalValue; arrow2 = () => this.secret1 + externalValue; render() { return this.arrow1() + ':' + this.arrow2() + ':' + this.secret2() + ':' + this.secret3(); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } } const proxy = createProxy(BaseClass); const Proxy = proxy.get(); const instance = new Proxy(); expect(instance.render()).toBe(42 + ':' + 43 + ':' + 0 + ':' + 1); { const externalValue = 24; class Update1Class extends React.Component { secret = 1; secret2 = generator2(); secret3 = generator3(); arrow1 = () => externalValue; arrow2 = () => this.secret1 + externalValue; render() { return this.arrow1() + ':' + this.arrow2() + ':' + this.secret2() + ':' + this.secret3(); } __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } } proxy.update(Update1Class); new Proxy(); } /* eslint-enable */ // Arrow1 function refer to external variable // Will not be updated // while Arrow2 function will // secret 3 should not be regenrated // secret 4(this inside) should be regenrated expect(instance.render()).toMatch(/([\d]+):25:0:1/); }); it('should stand-for all class members', () => { class Initial { constructor() { this.methodB = this.methodB.bind(this); } methodA() {} methodB() {} render() {} } const proxy = createProxy(Initial); const Class = proxy.get(); expect(Object.getOwnPropertyNames(Class.prototype)).toEqual( [ 'constructor', 'methodA', 'methodB', configuration.pureRender ? '' : 'render', 'hotComponentRender', 'hotComponentUpdate', 'componentDidMount', 'componentDidUpdate', 'componentWillUnmount', ].filter(Boolean), ); }); }); }); describe('proxy creation', () => { it('should wrap Component by Proxy', () => { class App extends React.Component { render() { return
    ; } } const Proxy = createProxy(App).get(); const instance = mount().instance(); expect(instance instanceof App).toBe(true); }); describe('should wrap SFC by SFC', () => { it('should wrap SFC by SFC Component', () => { const App = () =>
    ; const Proxy = createProxy(App).get(); expect('isStatelessFunctionalProxy' in Proxy).toBe(false); mount().instance(); expect(Proxy.isStatelessFunctionalProxy).toBe(configuration.pureSFC); }); it('should wrap SFC by SFC Pure', () => { const App = () =>
    ; configuration.pureSFC = true; const Proxy = createProxy(App).get(); expect('isStatelessFunctionalProxy' in Proxy).toBe(false); mount().instance(); configuration.pureSFC = false; expect(Proxy.isStatelessFunctionalProxy).toBe(true); }); }); it('should wrap SFC with Context by Proxy', () => { const App = () =>
    ; App.contextTypes = {}; const Proxy = createProxy(App).get(); expect('isStatelessFunctionalProxy' in Proxy).toBe(false); mount().instance(); expect(Proxy.isStatelessFunctionalProxy).toBe(false); }); it('should not update not constructed Proxies', () => { const spy1 = jest.fn(); const spy2 = jest.fn(); class App extends React.Component { constructor() { super(); spy1(); } render() { return
    ; } } const proxy = createProxy(App); expect(spy1).not.toHaveBeenCalled(); expect(spy1).not.toHaveBeenCalled(); { class App extends React.Component { constructor() { super(); spy2(); } render() { return
    ; } } proxy.update(App); expect(spy1).not.toHaveBeenCalled(); expect(spy1).not.toHaveBeenCalled(); const Proxy = proxy.get(); mount(); expect(spy1).toHaveBeenCalled(); expect(spy1).toHaveBeenCalled(); } }); it('should update constructed Proxies', () => { const spy1 = jest.fn(); const spy2 = jest.fn(); class App extends React.Component { constructor() { super(); spy1(); } render() { return
    ; } } const proxy = createProxy(App); const Proxy = proxy.get(); mount(); expect(spy1).toHaveBeenCalled(); expect(spy2).not.toHaveBeenCalled(); { class App extends React.Component { constructor() { super(); spy2(); } render() { return
    ; } } proxy.update(App); expect(spy1).toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); } }); }); describe('modern only', () => { it('sets up the constructor name from initial name', () => { const { Bar, Baz } = createFixtures().modern; const proxy = createProxy(Bar); const Proxy = proxy.get(); expect(Proxy.name).toBe('Bar'); proxy.update(Baz); expect(Proxy.name).toBe('Baz'); }); it('should not crash if new Function() throws', () => { const { Bar } = createFixtures().modern; const oldFunction = global.Function; global.Function = class extends oldFunction { constructor() { super(); throw new Error(); } }; try { expect(() => { const proxy = createProxy(Bar); const Proxy = proxy.get(); const barInstance = mount().instance(); expect(barInstance.constructor).toBe(Proxy); }).not.toThrow(); } finally { global.Function = oldFunction; } }); }); }); ================================================ FILE: test/proxy/helper.js ================================================ /* eslint-env browser */ /* eslint-disable react/no-render-return-value */ import ReactDOM from 'react-dom'; export function createMounter() { const DOMElement = document.createElement('div'); let internalMount; beforeEach(() => { internalMount = element => { const instance = ReactDOM.render(element, DOMElement); return { instance() { return instance; }, text() { return DOMElement.textContent; }, }; }; }); return { mount(element) { return internalMount(element); }, }; } export function ensureNoWarnings() { /* eslint-env jest */ let warnSpy; beforeEach(() => { warnSpy = jest.spyOn(console, 'warn'); }); afterEach(() => { expect(warnSpy).not.toHaveBeenCalled(); }); return { getWarnSpy() { return warnSpy; }, }; } ================================================ FILE: test/proxy/instance-descriptor.test.js ================================================ /* eslint-env jest */ /* eslint-disable no-underscore-dangle */ import React from 'react'; import { createMounter, ensureNoWarnings } from './helper'; import createProxy from '../../src/proxy'; const createFixtures = () => ({ modern: { InstanceDescriptor: class InstanceDescriptor extends React.Component { get answer() { return this.props.base + 42; } set something(value) { this._something = value * 2; } render() { return
    {this.answer || ''}
    ; } }, InstanceDescriptorUpdate: class InstanceDescriptorUpdate extends React.Component { get answer() { return this.props.base + 43; } set something(value) { this._something = value * 3; } render() { return
    {this.answer}
    ; } }, InstanceDescriptorRemoval: class InstanceDescriptorRemoval extends React.Component { render() { return
    {this.answer}
    ; } }, ThrowingAccessors: class ThrowingAccessors extends React.Component { get something() { throw new Error(); } set something(value) { throw new Error(); } render() { return null; } }, }, }); describe('instance descriptor', () => { ensureNoWarnings(); const { mount } = createMounter(); Object.keys(createFixtures()).forEach(type => { describe(type, () => { it('does not invoke accessors', () => { const { InstanceDescriptor, ThrowingAccessors } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); mount(); expect(() => proxy.update(ThrowingAccessors)).not.toThrow(); }); describe('getter', () => { it('is available on proxy class instance', () => { const { InstanceDescriptor } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('142'); expect(wrapper.instance().answer).toBe(142); }); it('gets added', () => { const { InstanceDescriptor, InstanceDescriptorRemoval } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptorRemoval); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe(''); proxy.update(InstanceDescriptor); const wrapper2 = mount(); expect(wrapper2.text()).toBe('142'); expect(wrapper2.instance().answer).toBe(142); }); it('gets replaced', () => { const { InstanceDescriptor, InstanceDescriptorUpdate, InstanceDescriptorRemoval } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); const instance = wrapper.instance(); expect(wrapper.text()).toBe('142'); proxy.update(InstanceDescriptorUpdate); const wrapper2 = mount(); expect(wrapper2.text()).toBe('143'); expect(instance.answer).toBe(143); proxy.update(InstanceDescriptorRemoval); const wrapper3 = mount(); expect(wrapper3.text()).toBe(''); expect(instance.answer).toBe(undefined); }); it('gets redefined', () => { const { InstanceDescriptor, InstanceDescriptorUpdate, InstanceDescriptorRemoval } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('142'); Object.defineProperty(wrapper.instance(), 'answer', { value: 7 }); proxy.update(InstanceDescriptorUpdate); mount(); expect(wrapper.text()).toBe('7'); expect(wrapper.instance().answer).toBe(7); proxy.update(InstanceDescriptorRemoval); expect(wrapper.text()).toBe('7'); expect(wrapper.instance().answer).toBe(7); }); }); describe('setter', () => { it('is available on proxy class instance', () => { const { InstanceDescriptor } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); wrapper.instance().something = 10; expect(wrapper.instance()._something).toBe(20); }); it('gets added', () => { const { InstanceDescriptor, InstanceDescriptorRemoval } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptorRemoval); const Proxy = proxy.get(); const wrapper = mount(); proxy.update(InstanceDescriptor); wrapper.instance().something = 10; expect(wrapper.instance()._something).toBe(20); }); it('gets replaced', () => { const { InstanceDescriptor, InstanceDescriptorUpdate, InstanceDescriptorRemoval } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); const instance = wrapper.instance(); instance.something = 10; expect(instance._something).toBe(20); proxy.update(InstanceDescriptorUpdate); expect(instance._something).toBe(20); instance.something = 10; expect(instance._something).toBe(30); proxy.update(InstanceDescriptorRemoval); expect(instance._something).toBe(30); instance.something = 7; expect(instance.something).toBe(7); expect(instance._something).toBe(30); }); it('gets redefined', () => { const { InstanceDescriptor, InstanceDescriptorUpdate } = createFixtures()[type]; const proxy = createProxy(InstanceDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('142'); Object.defineProperty(wrapper.instance(), 'something', { value: 50, }); proxy.update(InstanceDescriptorUpdate); expect(wrapper.instance().something).toBe(50); }); }); }); }); }); ================================================ FILE: test/proxy/instance-method.test.js ================================================ /* eslint-env jest */ import React from 'react'; import { createMounter, ensureNoWarnings } from './helper'; import createProxy from '../../src/proxy'; const createFixtures = () => ({ modern: { shouldWarnOnBind: false, Counter1x: class Counter1x extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } increment() { this.setState({ counter: this.state.counter + 1, }); } render() { return {this.state.counter}; } }, Counter10x: class Counter10x extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } increment() { this.setState({ counter: this.state.counter + 10, }); } render() { return {this.state.counter}; } }, Counter100x: class Counter100x extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } increment() { this.setState({ counter: this.state.counter + 100, }); } render() { return {this.state.counter}; } }, CounterWithoutIncrementMethod: class CounterWithoutIncrementMethod extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } render() { return {this.state.counter}; } }, NotPureComponent: class NotPureComponent extends React.Component { shouldComponentUpdate() { return true; } render() { return Component; } }, IsPureComponent: class IsPureComponent extends React.PureComponent { render() { return PureComponent; } }, }, }); describe('instance method', () => { const { getWarnSpy } = ensureNoWarnings(); const { mount } = createMounter(); Object.keys(createFixtures()).forEach(type => { describe(type, () => { it('gets added', () => { const { Counter1x, CounterWithoutIncrementMethod } = createFixtures()[type]; const proxy = createProxy(CounterWithoutIncrementMethod); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('0'); proxy.update(Counter1x); wrapper.instance().increment(); expect(wrapper.text()).toEqual('1'); }); it('gets replaced', () => { const { Counter1x, Counter10x, Counter100x } = createFixtures()[type]; const proxy = createProxy(Counter1x); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('0'); wrapper.instance().increment(); expect(wrapper.text()).toEqual('1'); proxy.update(Counter10x); wrapper.instance().increment(); mount(); expect(wrapper.text()).toEqual('11'); proxy.update(Counter100x); wrapper.instance().increment(); mount(); expect(wrapper.text()).toEqual('111'); }); it('removes shouldComponentUpdate', () => { const { IsPureComponent, NotPureComponent } = createFixtures()[type]; const proxy = createProxy(NotPureComponent); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('Component'); expect(wrapper.instance()).toHaveProperty('shouldComponentUpdate'); proxy.update(IsPureComponent); wrapper.instance().forceUpdate(); mount(); expect(wrapper.text()).toEqual('PureComponent'); expect(wrapper.instance()).not.toHaveProperty('shouldComponentUpdate'); }); it('cant handle bound methods', () => { const { Counter1x, Counter10x, shouldWarnOnBind } = createFixtures()[type]; const proxy = createProxy(Counter1x); const Proxy = proxy.get(); const wrapper = mount(); const instance = wrapper.instance(); getWarnSpy().mockReset(); const localWarnSpy = jest.spyOn(console, 'warn'); instance.increment = instance.increment.bind(instance); expect(localWarnSpy).toHaveBeenCalledTimes(shouldWarnOnBind ? 1 : 0); expect(wrapper.text()).toEqual('0'); instance.increment(); expect(wrapper.text()).toEqual('1'); proxy.update(Counter10x); instance.increment(); mount(); expect(wrapper.text()).toEqual('2'); // not 11 }); }); }); it('passes methods props thought', () => { const injectedMethod = (a, b) => this[24 + a + b]; injectedMethod.staticProp = 'magic'; class App extends React.Component { method() { return 42; } } App.prototype.injectedMethod = injectedMethod; const app1 = new App(); expect(app1.injectedMethod).toBe(injectedMethod); expect(app1.injectedMethod.staticProp).toBe('magic'); expect(String(app1.injectedMethod)).toBe(String(injectedMethod)); const Proxy = createProxy(App).get(); const app2 = new Proxy(); expect(app2.injectedMethod).not.toBe(injectedMethod); expect(app2.injectedMethod.staticProp).toBe('magic'); expect(app2.injectedMethod.length).toBe(2); expect(String(app2.injectedMethod)).toBe(String(injectedMethod)); }); }); ================================================ FILE: test/proxy/instance-property.test.js ================================================ /* eslint-env jest */ import React from 'react'; import { ensureNoWarnings, createMounter } from './helper'; import createProxy from '../../src/proxy'; const fixtures = () => ({ modern: { InstanceProperty: class InstanceProperty extends React.Component { answer = 42; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    {this.answer}
    ; } }, InstancePropertyUpdate: class InstancePropertyUpdate extends React.Component { answer = 43; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    {this.answer}
    ; } }, InstancePropertyRemoval: class InstancePropertyRemoval extends React.Component { /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    {this.answer}
    ; } }, InstancePropertyFromLocal: class InstanceProperty extends React.Component { getAnswer = () => this.answer; answer = 42; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    {this.getAnswer()}
    ; } }, InstancePropertyFromContext: class InstanceProperty extends React.Component { /* eslint-disable arrow-body-style */ getAnswer = () => { return this.answer; }; /* eslint-enable arrow-body-style */ answer = 42; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return
    {this.getAnswer()}
    ; } }, }, }); describe('instance property', () => { ensureNoWarnings(); const { mount } = createMounter(); Object.keys(fixtures).forEach(type => { describe(type, () => { const { InstanceProperty, InstancePropertyUpdate, InstancePropertyRemoval } = fixtures()[type]; it('is available on proxy class instance', () => { const proxy = createProxy(InstanceProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(wrapper.instance().answer).toBe(42); }); it('is left unchanged when reassigned', () => { const proxy = createProxy(InstanceProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); wrapper.instance().answer = 100; proxy.update(InstancePropertyUpdate); mount(); expect(wrapper.text()).toBe('43'); expect(wrapper.instance().answer).toBe(43); proxy.update(InstancePropertyRemoval); mount(); expect(wrapper.text()).toBe('43'); expect(wrapper.instance().answer).toBe(43); }); /** * I'm not aware of any way of retrieving their new values * without calling the constructor, which seems like too much * of a side effect. We also don't want to overwrite them * in case they changed. */ it('is left unchanged even if not reassigned (known limitation)', () => { const proxy = createProxy(InstanceProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); proxy.update(InstancePropertyUpdate); mount(); expect(wrapper.text()).toBe('43'); expect(wrapper.instance().answer).toBe(43); proxy.update(InstancePropertyRemoval); mount(); expect(wrapper.text()).toBe('43'); expect(wrapper.instance().answer).toBe(43); }); }); }); describe('ES6 property', () => { // untestable without real arrow functions // it('show use the underlayer instance value', () => { // const proxy = createProxy(fixtures.modern.InstancePropertyFromLocal); // const Proxy = proxy.get(); // const instance = renderer.render(); // expect(renderer.getRenderOutput().props.children).toBe(42); // instance.answer = 100; // renderer.render(); // expect(renderer.getRenderOutput().props.children).toBe(42); // }) it('show use the underlayer top value', () => { const proxy = createProxy(fixtures().modern.InstancePropertyFromContext); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); wrapper.instance().answer = 100; mount(); expect(wrapper.text()).toBe('100'); }); }); }); ================================================ FILE: test/proxy/lifecycle-method.test.js ================================================ /* eslint-env jest */ import React, { Component } from 'react'; import { createMounter } from './helper'; import createProxy from '../../src/proxy'; import '../../src/index.dev'; import { configureGeneration } from '../../src/global/generation'; describe('lifecycle method', () => { beforeEach(() => { configureGeneration(1, 1); }); const { mount } = createMounter(); class Controller extends Component { state = { update: 1, }; render() { const { Proxy } = this.props; return ; } } const testFabric = methodName => (Component, patchedRender, spy) => { class App1 extends Component { constructor() { super(); this.secret = 1; this.superSecret = 42; } [methodName]() { spy(); const oldRender = this.render.bind(this); this.render = () => { this.superSecret = this.secret + 1; return patchedRender.call(this, oldRender); }; } render() { return
    {this.superSecret}
    ; } } class App2 extends Component { constructor() { super(); this.secret = 2; } [methodName]() { spy(); } render() { return
    !{this.superSecret * 5}
    ; } } return { App1, App2 }; }; const getTestClass = (methodName, spy) => { function patchedRender(oldRender) { return
    PATCHED + {oldRender()}
    ; } return testFabric(methodName)(Component, patchedRender, spy); }; // false test it.skip('handle componentWillMount', done => { const spy = jest.fn(); const { App1, App2 } = getTestClass('componentWillMount', spy); const proxy = createProxy(App1); const Proxy = proxy.get(); const wrapper = mount(); expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.text()).toContain('PATCHED + 2'); proxy.update(App2); wrapper.instance().forceUpdate(); expect(spy).toHaveBeenCalledTimes(1); // first render before hot render expect(wrapper.text()).toContain('PATCHED + !10'); wrapper.instance().forceUpdate(); expect(wrapper.text()).toContain('PATCHED + !15'); done(); }); // false test it.skip('handle componentDidMount', () => { const spy = jest.fn(); const { App1, App2 } = getTestClass('componentDidMount', spy); const proxy = createProxy(App1); const Proxy = proxy.get(); const wrapper = mount(); expect(spy).toHaveBeenCalledTimes(1); expect(wrapper.text()).toContain('42'); proxy.update(App2); wrapper.instance().forceUpdate(); expect(spy).toHaveBeenCalledTimes(1); // first render before hot render expect(wrapper.text()).toContain('PATCHED + !10'); wrapper.instance().forceUpdate(); expect(wrapper.text()).toContain('PATCHED + !15'); }); it('handle dynamic method creation', () => { class App1 extends Component { method1 = () => 41 + this.var1; var1 = 1; render() { return
    {this.method1()}
    ; } } class App2 extends Component { method2 = () => 22 + this.var2; var2 = 2; /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { return (
    {this.method1()} + {this.method2()}
    ); } } const proxy = createProxy(App1); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toContain('42'); proxy.update(App2); wrapper.instance().forceUpdate(); // first render before hot render expect(wrapper.text()).toContain('42'); wrapper.instance().forceUpdate(); // both methods expected to be present expect(wrapper.text()).toContain('42 + 24'); }); }); ================================================ FILE: test/proxy/static-descriptor.test.js ================================================ /* eslint-env jest */ /* eslint-disable no-underscore-dangle */ import React from 'react'; import { ensureNoWarnings, createMounter } from './helper'; import createProxy from '../../src/proxy'; const createFixtures = () => ({ modern: { StaticDescriptor: class StaticDescriptor extends React.Component { static get answer() { return 42; } static set something(value) { this._something = value * 2; } render() { return
    {this.constructor.answer}
    ; } }, StaticDescriptorUpdate: class StaticDescriptorUpdate extends React.Component { static get answer() { return 43; } static set something(value) { this._something = value * 3; } render() { return
    {this.constructor.answer}
    ; } }, StaticDescriptorRemoval: class StaticDescriptorRemoval extends React.Component { render() { return
    {this.constructor.answer}
    ; } }, ThrowingAccessors: class ThrowingAccessors extends React.Component { static get something() { throw new Error(); } static set something(value) { throw new Error(); } render() { return null; } }, }, }); describe('static descriptor', () => { ensureNoWarnings(); const { mount } = createMounter(); Object.keys(createFixtures()).forEach(type => { let fixtures; describe(type, () => { beforeEach(() => { fixtures = createFixtures()[type]; }); it('does not invoke accessors', () => { const { StaticDescriptor, ThrowingAccessors } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); mount(); expect(() => proxy.update(ThrowingAccessors)).not.toThrow(); }); describe('getter', () => { beforeEach(() => { fixtures = createFixtures()[type]; }); it('is available on proxy class', () => { const { StaticDescriptor } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('42'); expect(wrapper.instance().constructor.answer).toEqual(42); expect(Proxy.answer).toEqual(42); }); it('gets added', () => { const { StaticDescriptor, StaticDescriptorRemoval } = fixtures; const proxy = createProxy(StaticDescriptorRemoval); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual(''); proxy.update(StaticDescriptor); mount(); expect(wrapper.text()).toEqual('42'); expect(wrapper.instance().constructor.answer).toEqual(42); }); it('gets replaced', () => { const { StaticDescriptor, StaticDescriptorUpdate, StaticDescriptorRemoval } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('42'); proxy.update(StaticDescriptorUpdate); mount(); expect(wrapper.text()).toEqual('43'); expect(wrapper.instance().constructor.answer).toEqual(43); proxy.update(StaticDescriptorRemoval); mount(); expect(wrapper.text()).toEqual(''); expect(wrapper.instance().answer).toEqual(undefined); }); it('gets redefined', () => { const { StaticDescriptor, StaticDescriptorUpdate, StaticDescriptorRemoval } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('42'); Object.defineProperty(wrapper.instance().constructor, 'answer', { value: 7, }); proxy.update(StaticDescriptorUpdate); mount(); expect(wrapper.text()).toEqual('7'); expect(wrapper.instance().constructor.answer).toEqual(7); proxy.update(StaticDescriptorRemoval); mount(); expect(wrapper.text()).toEqual('7'); expect(wrapper.instance().constructor.answer).toEqual(7); }); }); describe('setter', () => { beforeEach(() => { fixtures = createFixtures()[type]; }); it('is available on proxy class instance', () => { const { StaticDescriptor } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); wrapper.instance().constructor.something = 10; }); it('gets added', () => { const { StaticDescriptor, StaticDescriptorRemoval } = fixtures; const proxy = createProxy(StaticDescriptorRemoval); const Proxy = proxy.get(); const wrapper = mount(); proxy.update(StaticDescriptor); wrapper.instance().constructor.something = 10; expect(wrapper.instance().constructor._something).toEqual(20); }); it('gets replaced', () => { const { StaticDescriptor, StaticDescriptorUpdate, StaticDescriptorRemoval } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); wrapper.instance().constructor.something = 10; expect(wrapper.instance().constructor._something).toEqual(20); proxy.update(StaticDescriptorUpdate); expect(wrapper.instance().constructor._something).toEqual(20); wrapper.instance().constructor.something = 10; expect(wrapper.instance().constructor._something).toEqual(30); proxy.update(StaticDescriptorRemoval); expect(wrapper.instance().constructor._something).toEqual(30); console.error = jest.fn(); expect(() => { wrapper.instance().constructor.something = 7; }).toThrow(); expect(wrapper.instance().constructor._something).toEqual(30); }); it('gets redefined', () => { const { StaticDescriptor, StaticDescriptorUpdate } = fixtures; const proxy = createProxy(StaticDescriptor); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toEqual('42'); Object.defineProperty(wrapper.instance().constructor, 'something', { value: 50, }); proxy.update(StaticDescriptorUpdate); expect(wrapper.instance().constructor.something).toEqual(50); }); }); }); }); }); describe('static functional descriptor', () => { it('shout pass propTypes', () => { const TestComponent = ({ property }) =>
    {property}
    ; TestComponent.propTypes = { property: 42, }; TestComponent.displayName = 'Testing'; const proxy = createProxy(TestComponent); const Proxy = proxy.get(); expect(Proxy.propTypes.property).toBe(42); expect(Proxy.displayName).toBe('Testing'); }); }); ================================================ FILE: test/proxy/static-method.test.js ================================================ /* eslint-env jest */ import React from 'react'; import { ensureNoWarnings, createMounter } from './helper'; import createProxy from '../../src/proxy'; import reactHotLoader from '../../src/reactHotLoader'; const fixtures = () => ({ modern: { StaticMethod: class StaticMethod extends React.Component { static getAnswer() { return 42; } render() { return
    {this.constructor.getAnswer()}
    ; } }, StaticMethodUpdate: class StaticMethodUpdate extends React.Component { static getAnswer() { return 43; } render() { return
    {this.constructor.getAnswer()}
    ; } }, StaticMethodRemoval: class StaticMethodRemoval extends React.Component { render() { return
    {this.constructor.getAnswer()}
    ; } }, }, }); describe('static method', () => { ensureNoWarnings(); const { mount } = createMounter(); Object.keys(fixtures()).forEach(type => { describe(type, () => { const { StaticMethod, StaticMethodUpdate, StaticMethodRemoval } = fixtures()[type]; beforeEach(() => reactHotLoader.reset()); it('is available on proxy class instance', () => { const proxy = createProxy(StaticMethod); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.getAnswer()).toBe(42); }); it('is own on proxy class instance', () => { const proxy = createProxy(StaticMethod); const Proxy = proxy.get(); expect(Proxy.hasOwnProperty('getAnswer')).toBe(true); }); it('gets added', () => { const proxy = createProxy(StaticMethodRemoval); const Proxy = proxy.get(); expect(Proxy.getAnswer).toBe(undefined); proxy.update(StaticMethod); try { // will throw error in es2015 mode const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.getAnswer()).toBe(42); } catch (e) { // ES2015 error } }); it('gets replaced', () => { const proxy = createProxy(StaticMethod); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.getAnswer()).toBe(42); proxy.update(StaticMethodUpdate); mount(); expect(wrapper.text()).toBe('43'); expect(Proxy.getAnswer()).toBe(43); }); it('get replaced if bound', () => { const proxy = createProxy(StaticMethod); const Proxy = proxy.get(); Proxy.getAnswer = Proxy.getAnswer.bind(Proxy); const { getAnswer } = Proxy; const wrapper = mount(); expect(wrapper.text()).toBe('42'); proxy.update(StaticMethodUpdate); mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.getAnswer()).toBe(42); expect(getAnswer()).toBe(42); }); it('is detached if deleted', () => { const proxy = createProxy(StaticMethod); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.getAnswer()).toBe(42); proxy.update(StaticMethodRemoval); console.error = jest.fn(); // Waiting for a fix in Jest // expect(() => wrapper.instance().forceUpdate()).toThrow() // expect(() => mount()).toThrow() expect(Proxy.getAnswer).toBe(undefined); }); }); }); }); ================================================ FILE: test/proxy/static-property.test.js ================================================ /* eslint-env jest */ /* eslint-disable react/no-unused-prop-types */ import React from 'react'; import PropTypes from 'prop-types'; import { ensureNoWarnings, createMounter } from './helper'; import createProxy from '../../src/proxy'; import reactHotLoader from '../../src/reactHotLoader'; const fixtures = () => ({ modern: { StaticProperty: class StaticProperty extends React.Component { static answer = 42; render() { return
    {this.constructor.answer}
    ; } }, StaticPropertyUpdate: class StaticPropertyUpdate extends React.Component { static answer = 43; render() { return
    {this.constructor.answer}
    ; } }, StaticPropertyRemoval: class StaticPropertyRemoval extends React.Component { render() { return
    {this.constructor.answer}
    ; } }, WithPropTypes: class WithPropTypes extends React.Component { static propTypes = { something: PropTypes.number, }; static contextTypes = { something: PropTypes.number, }; static childContextTypes = { something: PropTypes.number, }; render() { return null; } }, WithPropTypesUpdate: class WithPropTypesUpdate extends React.Component { static propTypes = { something: PropTypes.string, }; static contextTypes = { something: PropTypes.string, }; static childContextTypes = { something: PropTypes.string, }; render() { return null; } }, }, }); describe('static property', () => { ensureNoWarnings(); const { mount } = createMounter(); Object.keys(fixtures()).forEach(type => { describe(type, () => { const { StaticProperty, StaticPropertyUpdate, StaticPropertyRemoval, WithPropTypes, WithPropTypesUpdate, } = fixtures()[type]; beforeEach(() => reactHotLoader.reset()); it('is available on proxy class instance', () => { const proxy = createProxy(StaticProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); expect(Proxy.answer).toBe(42); }); it('is own on proxy class instance', () => { const proxy = createProxy(StaticProperty); const Proxy = proxy.get(); expect(Proxy.hasOwnProperty('answer')).toBe(true); }); it('is changed when not reassigned', () => { const proxy = createProxy(StaticProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); proxy.update(StaticPropertyUpdate); mount(); expect(wrapper.text()).toBe('43'); expect(Proxy.answer).toBe(43); proxy.update(StaticPropertyRemoval); mount(); expect(wrapper.text()).toBe(''); expect(Proxy.answer).toBe(undefined); }); it('is changed for propTypes, contextTypes, childContextTypes', () => { const proxy = createProxy(WithPropTypes); const PropTypesProxy = proxy.get(); expect(PropTypesProxy.propTypes.something).toBe(PropTypes.number); expect(PropTypesProxy.contextTypes.something).toBe(PropTypes.number); expect(PropTypesProxy.childContextTypes.something).toBe(PropTypes.number); proxy.update(WithPropTypesUpdate); expect(PropTypesProxy.propTypes.something).toBe(PropTypes.string); expect(PropTypesProxy.contextTypes.something).toBe(PropTypes.string); expect(PropTypesProxy.childContextTypes.something).toBe(PropTypes.string); }); /** * Sometimes people dynamically store stuff on statics. */ it('is not changed when reassigned', () => { const proxy = createProxy(StaticProperty); const Proxy = proxy.get(); const wrapper = mount(); expect(wrapper.text()).toBe('42'); Proxy.answer = 100; proxy.update(StaticPropertyUpdate); mount(); expect(wrapper.text()).toBe('100'); expect(Proxy.answer).toBe(100); proxy.update(StaticPropertyRemoval); mount(); expect(wrapper.text()).toBe('100'); expect(Proxy.answer).toBe(100); }); }); }); }); ================================================ FILE: test/proxy/unmounting.test.js ================================================ /* eslint-env jest */ /* eslint-disable react/no-render-return-value */ import React from 'react'; import { ensureNoWarnings, createMounter } from './helper'; import createProxy from '../../src/proxy'; const createFixtures = () => ({ modern: { Bar: class Bar extends React.Component { componentWillUnmount() { this.didUnmount = true; } render() { return
    Bar
    ; } }, Baz: class Baz extends React.Component { componentWillUnmount() { this.didUnmount = true; } render() { return
    Baz
    ; } }, Foo: class Foo extends React.Component { componentWillUnmount() { this.didUnmount = true; } render() { return
    Foo
    ; } }, }, }); describe('unmounting', () => { let fixtures = createFixtures(); ensureNoWarnings(); const { mount } = createMounter(); beforeEach(() => { fixtures = createFixtures(); }); Object.keys(fixtures).forEach(type => { describe(type, () => { it('happens without proxy', () => { const { Bar, Baz } = fixtures[type]; const barWrapper = mount(); const barInstance = barWrapper.instance(); expect(barWrapper.text()).toBe('Bar'); const bazWrapper = mount(); const bazInstance = bazWrapper.instance(); expect(bazWrapper.text()).toBe('Baz'); expect(barInstance).not.toBe(bazInstance); expect(barInstance.didUnmount).toBe(true); }); it('does not happen when rendering new proxied versions', () => { const { Bar, Baz, Foo } = fixtures[type]; const proxy = createProxy(Bar); const BarProxy = proxy.get(); const barWrapper = mount(); const barInstance = barWrapper.instance(); expect(barWrapper.text()).toBe('Bar'); expect(barInstance.didUnmount).toBe(undefined); proxy.update(Baz); const BazProxy = proxy.get(); const bazWrapper = mount(); const bazInstance = bazWrapper.instance(); expect(bazWrapper.text()).toBe('Baz'); expect(barInstance).toBe(bazInstance); expect(barInstance.didUnmount).toBe(undefined); proxy.update(Foo); const FooProxy = proxy.get(); const fooWrapper = mount(); const fooInstance = fooWrapper.instance(); expect(fooWrapper.text()).toBe('Foo'); expect(barInstance).toBe(fooInstance); expect(barInstance.didUnmount).toBe(undefined); }); it('does not happen when rendering old proxied versions', () => { const { Bar, Baz, Foo } = fixtures[type]; const proxy = createProxy(Bar); const Proxy = proxy.get(); const barWrapper = mount(); expect(barWrapper.text()).toBe('Bar'); expect(barWrapper.instance().didUnmount).toBe(undefined); proxy.update(Baz); const bazWrapper = mount(); expect(bazWrapper.text()).toBe('Baz'); expect(barWrapper.instance()).toBe(bazWrapper.instance()); expect(barWrapper.instance().didUnmount).toBe(undefined); proxy.update(Foo); const fooWrapper = mount(); expect(fooWrapper.text()).toBe('Foo'); expect(barWrapper.instance()).toBe(fooWrapper.instance()); expect(barWrapper.instance().didUnmount).toBe(undefined); }); }); }); }); ================================================ FILE: test/reactHotLoader.test.js ================================================ import React from 'react'; import { mount } from 'enzyme'; import { PROXY_KEY, UNWRAP_PROXY } from '../src/proxy/constants'; import { get as getGeneration } from '../src/global/generation'; import reactHotLoader from '../src/reactHotLoader'; import { internalConfiguration } from '../src/configuration'; describe('reactHotLoader', () => { let Div; let Span; beforeEach(() => { Div = () =>
    ; Span = () => ; reactHotLoader.patch(React); reactHotLoader.reset(); }); describe('#patch', () => { let OriginalReactMock; let ReactMock; beforeEach(() => { OriginalReactMock = { createElement: jest.fn(), cloneElement: jest.fn(), createFactory: jest.fn(), Children: { only: jest.fn(x => x), }, }; ReactMock = { ...OriginalReactMock }; }); it('should patch all methods', () => { reactHotLoader.patch(ReactMock); expect(ReactMock.createElement.isPatchedByReactHotLoader).toBe(true); expect(ReactMock.cloneElement.isPatchedByReactHotLoader).toBe(true); expect(ReactMock.createFactory.isPatchedByReactHotLoader).toBe(true); expect(ReactMock.Children.only.isPatchedByReactHotLoader).toBe(true); }); describe('#createElement', () => { it('should create a proxy and call original method with it', () => { reactHotLoader.patch(ReactMock); ReactMock.createElement(Div, { foo: 'bar' }); const DivProxy = OriginalReactMock.createElement.mock.calls[0][0]; expect(DivProxy[PROXY_KEY]).toBeDefined(); }); it('null case', () => { reactHotLoader.patch(ReactMock); const result = React.createElement(undefined); expect(result.type).toBeUndefined(); }); }); describe('#createFactory', () => { it('should create a factory that returns proxy', () => { reactHotLoader.patch(ReactMock); const dummyFactory = ReactMock.createFactory(Div); dummyFactory({ foo: 'bar' }); const DivProxy = OriginalReactMock.createElement.mock.calls[0][0]; expect(DivProxy[PROXY_KEY]).toBeDefined(); }); }); describe('#Children.only', () => { it('should returns a proxy', () => { reactHotLoader.patch(ReactMock); const children = { type: Div, props: { foo: 'bar' } }; const result = ReactMock.Children.only(children); const DivProxy = result.type; expect(DivProxy[PROXY_KEY]).toBeDefined(); }); }); }); describe('#reset', () => { it('should reset all proxies', () => { const proxyElement = React.createElement(Div, { foo: 'bar' }); const secondProxyElement = React.createElement(Div, { foo: 'bar' }); expect(proxyElement.type[PROXY_KEY]).toBe(secondProxyElement.type[PROXY_KEY]); // After that, a new proxy key should be generated // meaning that a new proxy has been created reactHotLoader.reset(); const thirdProxyElement = React.createElement(Div, { foo: 'bar' }); expect(proxyElement.type[PROXY_KEY]).not.toBe(thirdProxyElement.type[PROXY_KEY]); }); }); describe('#disableProxyCreation', () => { afterEach(() => { internalConfiguration.disableProxyCreation = false; }); it('should disable the creation of proxy', () => { internalConfiguration.disableProxyCreation = true; const proxyElement = React.createElement(Div, { foo: 'bar' }); expect(proxyElement.type[PROXY_KEY]).not.toBeDefined(); }); it('should still be possible to get existing proxies', () => { React.createElement(Div, { foo: 'bar' }); internalConfiguration.disableProxyCreation = true; const proxyElement = React.createElement(Div, { foo: 'bar' }); expect(proxyElement.type[PROXY_KEY]).toBeDefined(); }); }); describe('#register', () => { it('should increment update counter', () => { const oldGeneration = getGeneration(); reactHotLoader.register(Div, 'Div', 'reactHotLoader.test.js'); // new thing, no change expect(getGeneration()).toBe(oldGeneration + 1); reactHotLoader.register(Div, 'Div', 'reactHotLoader.test.js'); // no replacement expect(getGeneration()).toBe(oldGeneration + 2); const NewDiv = () =>
    ; reactHotLoader.register(NewDiv, 'Div', 'reactHotLoader.test.js'); // replacement! expect(getGeneration()).toBe(oldGeneration + 3); }); it('should ignore dom elements and incomplete signature', () => { reactHotLoader.register(Div, 'Div', 'reactHotLoader.test.js'); reactHotLoader.register('div', 'Div', 'reactHotLoader.test.js'); reactHotLoader.register(Span, 'Div'); reactHotLoader.register(Span, ''); reactHotLoader.register(Span, '', ''); const proxyElement = React.createElement(Div); expect(proxyElement.type[UNWRAP_PROXY]()).toBe(Div); }); it('should update proxy', () => { reactHotLoader.register(Div, 'Div', 'reactHotLoader.test.js'); const proxyElement = React.createElement(Div); expect(proxyElement.type[UNWRAP_PROXY]()).toBe(Div); reactHotLoader.register(Span, 'Div', 'reactHotLoader.test.js'); expect(proxyElement.type[UNWRAP_PROXY]()).toBe(Span); }); it('should not double-proxy', () => { const Component1 = () =>
    42
    ; const Element1 = ; const Type1 = Element1.type; const Element2 = ; const Element3 = React.Children.only(Element1); const Element4 = React.cloneElement(Element1); expect(Element1.type).toBe(Element2.type); expect(Element1.type).toBe(Element3.type); expect(Element1.type).toBe(Element4.type); }); it('should result into shadowing the original component', () => { // Registering Div reactHotLoader.register(Div, 'Div', 'reactHotLoader.test.js'); // Creating a Div element, proxy of Div is used let proxyElement = React.createElement(Div); let wrapper = mount(proxyElement); expect(wrapper.html()).toBe('
    '); // Register the same component but with a Span reactHotLoader.register(Span, 'Div', 'reactHotLoader.test.js'); // Creating a Div element, proxy of Span is used proxyElement = React.createElement(Div); wrapper = mount(proxyElement); expect(wrapper.html()).toBe(''); }); }); }); ================================================ FILE: test/reconciler/proxyAdapter.test.js ================================================ /* eslint-env browser */ import { proxyWrapper } from '../../src/reconciler/proxyAdapter'; import * as proxies from '../../src/reconciler/proxies'; import { unscheduleUpdate } from '../../src/reconciler/hotReplacementRender'; import { internalConfiguration } from '../../src/configuration'; jest.mock('../../src/reconciler/proxies'); jest.mock('../../src/reconciler/hotReplacementRender'); proxies.getProxyByType.mockReturnValue({ get: () => 'proxy' }); describe(`proxyAdapter`, () => { const fn = () => {}; it('should handle empty result', () => { expect(proxyWrapper()).toBe(undefined); expect(proxyWrapper(null)).toBe(null); }); it('should handle arrays', () => { expect(proxyWrapper([1, 2, 3])).toEqual([1, 2, 3]); expect(proxyWrapper([{ type: fn, prop: 42 }])).toEqual([{ type: 'proxy', prop: 42 }]); }); it('should handle elements', () => { expect(proxyWrapper({ type: fn, prop: 42 })).toEqual({ type: 'proxy', prop: 42, }); }); it('should remove rendered proxy', () => { const object = {}; unscheduleUpdate.mockClear(); internalConfiguration.disableProxyCreation = 1; proxyWrapper.call(object); expect(unscheduleUpdate).not.toHaveBeenCalled(); internalConfiguration.disableProxyCreation = 0; proxyWrapper.call(object); expect(unscheduleUpdate).toHaveBeenCalledWith(object); }); }); ================================================ FILE: test/reconciler.test.js ================================================ import React, { Component } from 'react'; import { mount } from 'enzyme'; import TestRenderer from 'react-test-renderer'; import { AppContainer } from '../src/index.dev'; import { openGeneration, closeGeneration, configureGeneration, increment as incrementGeneration, } from '../src/global/generation'; import { areComponentsEqual } from '../src/utils.dev'; import logger from '../src/logger'; import reactHotLoader from '../src/reactHotLoader'; import configuration, { internalConfiguration } from '../src/configuration'; jest.mock('../src/logger'); const spyComponent = (render, displayName, key) => { const mounted = jest.fn(); const unmounted = jest.fn(); const willUpdate = jest.fn(); const rendered = jest.fn(); class TestingComponent extends Component { componentWillMount() { mounted(); } componentWillUpdate(nextProps, nextState) { willUpdate(nextProps, nextState, this.props, this.state); } componentWillUnmount() { unmounted(); } /* eslint-disable */ __reactstandin__regenerateByEval(key, code) { this[key] = eval(code); } /* eslint-enable */ render() { rendered(); return render(this.props); } } if (displayName) { TestingComponent.displayName = displayName; } return { Component: TestingComponent, willUpdate, mounted, unmounted, key, rendered, }; }; describe('reconciler', () => { describe('Application', () => { beforeEach(() => { configureGeneration(1, 1); }); it('should regenerate internal component', () => { const root = spyComponent(({ children }) =>
    {children}
    , 'root', 'root'); const first = spyComponent(({ children }) => FIRST {children}, 'test', '1'); const second = spyComponent(() => REPLACED, 'test', '2'); const third = spyComponent(() => NEW ONE, 'somethingElse', '3'); let currentComponent = first; const currentProps = {}; let renderTick = 0; const ComponentSwap = props => { const { Component, key } = currentComponent; return (
    {key} + {renderTick++}
    ); }; const App = () => ( 42 ); App.contextTypes = {}; const wrapper = mount( , ); // mount and perform first checks expect(wrapper.find(.type).length).toBe(1); expect(root.mounted).toHaveBeenCalledTimes(1); expect(first.mounted).toHaveBeenCalledTimes(1); expect(wrapper.text()).toMatch(/FIRST/); // replace with `the same` component currentComponent = second; // they are different expect(areComponentsEqual(first.Component, second.Component)).toBe(false); currentProps.newProp = true; incrementGeneration(); wrapper.setProps({ update: 'now' }); // now react-stand-in merge them together expect(areComponentsEqual(first.Component, second.Component)).toBe(true); expect(wrapper.find(.type).length).toBe(1); expect(wrapper.find(.type).length).toBe(1); expect(wrapper.text()).not.toMatch(/FIRST/); expect(wrapper.text()).toMatch(/REPLACED/); expect(root.mounted).toHaveBeenCalledTimes(1); expect(first.unmounted).toHaveBeenCalledTimes(0); expect(second.mounted).toHaveBeenCalledTimes(0); // expect(second.willUpdate).toHaveBeenCalledTimes(2) // what props should be used? Look like the new ones expect(second.willUpdate.mock.calls[0]).toEqual([ { children: '42', keyId: '1' }, null, { children: '42', keyId: '1' }, null, ]); expect(second.willUpdate.mock.calls[1]).toEqual([ { children: '42', newProp: true, keyId: '2' }, null, { children: '42', keyId: '1', ...(React.memo ? { cacheBusterProp: true } : {}), }, null, ]); // replace with a different component currentComponent = third; incrementGeneration(); wrapper.setProps({ update: 'now' }); expect(wrapper.update().find(.type).length).toBe(1); // first will never be unmounted expect(first.unmounted).toHaveBeenCalledTimes(0); expect(second.unmounted).toHaveBeenCalledTimes(1); expect(third.mounted).toHaveBeenCalledTimes(1); expect(areComponentsEqual(first.Component, third.Component)).toBe(false); expect(areComponentsEqual(second.Component, third.Component)).toBe(false); }); it('should hot-swap only internal components', () => { let An0; let An1; let Bn0; let Bn1; let App; { const A = () =>
    A
    ; const B = () =>
    A
    ; A.displayName = 'A'; B.displayName = 'B'; App = () => ( ); An0 = A; Bn0 = B; reactHotLoader.register(App, 'App', 'test-hot-swap.js'); reactHotLoader.register(B, 'B0', 'test-hot-swap.js'); } const wrapper = mount( , ); { const A = () =>
    A
    ; const B = () =>
    A
    ; A.displayName = 'A'; B.displayName = 'B'; App = () => (
    ); An1 = A; Bn1 = B; reactHotLoader.register(App, 'App', 'test-hot-swap.js'); reactHotLoader.register(B, 'B1', 'test-hot-swap.js'); } incrementGeneration(); wrapper.setProps({ update: 'now' }); // A-s are similar, and got merged expect(.type).toEqual(.type); // B-s are simlar, but known to be different types - not merged expect(.type).not.toEqual(.type); }); it('should regenerate internal component without AppContainer', () => { const first = spyComponent(({ children }) => {children}, 'test', '1'); const second = spyComponent(() => REPLACED, 'test', '2'); let currentComponent = first; const ComponentSwap = props => { const { Component } = currentComponent; return (
    ); }; const App = () => (

    working

    42
    ); const wrapper = mount( // ensure - there is no AppContainer , // , ); expect(wrapper.html()).not.toContain('REPLACED'); openGeneration(); currentComponent = second; incrementGeneration(); wrapper.setProps({ update: 'now' }); closeGeneration(); expect(wrapper.html()).toContain('REPLACED'); expect(first.unmounted).toHaveBeenCalledTimes(0); expect(second.mounted).toHaveBeenCalledTimes(0); }); it('should use new children branch during reconcile', () => { const First = spyComponent(() => 1, 'test', '1'); const Second = spyComponent(() => 2, 'test', '2'); const App = ({ second }) => (
    {second && }
    ); const Mounter = ({ second }) => ; Mounter.contextTypes = {}; const wrapper = mount(); expect(First.rendered).toHaveBeenCalledTimes(1); expect(Second.rendered).toHaveBeenCalledTimes(1); incrementGeneration(); wrapper.setProps({ update: 'now' }); const renderCompensation = configuration.pureRender ? 1 : 0; expect(First.rendered).toHaveBeenCalledTimes(3 + renderCompensation); expect(Second.rendered).toHaveBeenCalledTimes(3 + renderCompensation); openGeneration(); incrementGeneration(); wrapper.setProps({ second: false }); closeGeneration(); expect(First.rendered).toHaveBeenCalledTimes(5 + renderCompensation); expect(Second.rendered).toHaveBeenCalledTimes(3 + renderCompensation); expect(First.unmounted).toHaveBeenCalledTimes(0); expect(Second.unmounted).toHaveBeenCalledTimes(1); }); it('should use new children branch during reconcile for full components', () => { const First = spyComponent(() => 1, 'test', '1'); const Second = spyComponent(() => 2, 'test', '2'); const Section = ({ children }) =>
    {children}
    ; const App = ({ second }) => (
    {second && }
    ); const Mounter = ({ second }) => ; Mounter.contextTypes = {}; const wrapper = mount(); expect(First.rendered).toHaveBeenCalledTimes(1); expect(Second.rendered).toHaveBeenCalledTimes(1); incrementGeneration(); wrapper.setProps({ update: 'now' }); expect(First.rendered).toHaveBeenCalledTimes(configuration.pureRender ? 4 : 3); expect(Second.rendered).toHaveBeenCalledTimes(configuration.pureRender ? 4 : 3); incrementGeneration(); wrapper.setProps({ second: false }); expect(First.rendered).toHaveBeenCalledTimes(configuration.pureRender ? 7 : 5); expect(Second.rendered).toHaveBeenCalledTimes(configuration.pureRender ? 4 : 3); expect(First.unmounted).toHaveBeenCalledTimes(0); expect(Second.unmounted).toHaveBeenCalledTimes(1); }); it('should handle child mounting', () => { const First = spyComponent(() => test1, 'test1', '1'); const Second = spyComponent(() => test2, 'test2', '2'); const Third = spyComponent(() => test3, 'test3', '3'); const App = ({ first, second, third }) => (
    {first && } {second && [
    start
    , ,
    middle
    , third && , ]}
    ); App.contextTypes = {}; const wrapper = mount(); expect(First.rendered).toHaveBeenCalledTimes(0); openGeneration(); incrementGeneration(); wrapper.setProps({ first: true }); closeGeneration(); expect(First.rendered).toHaveBeenCalledTimes(1); // 1. prev state was empty == no need to reconcile openGeneration(); incrementGeneration(); wrapper.setProps({ second: true }); closeGeneration(); expect(First.rendered).toHaveBeenCalledTimes(3); // +3 (reconcile + update + render) expect(Second.rendered).toHaveBeenCalledTimes(1); // (update from first + render) wrapper.setProps({ third: true }); expect(First.rendered).toHaveBeenCalledTimes(4); expect(Second.rendered).toHaveBeenCalledTimes(2); expect(Third.rendered).toHaveBeenCalledTimes(1); expect(wrapper.update().html()).toMatch(/test3/); }); it('should handle function as a child', () => { const first = spyComponent(({ children }) => {children(0)}, 'test', '1'); const second = spyComponent(({ children }) => {children(1)}, 'test', '2'); let currentComponent = first; const ComponentSwap = props => { const { Component } = currentComponent; return (
    ); }; const App = () => (

    working

    {x => 42 + x}
    ); const wrapper = mount( , ); expect(wrapper.text()).toContain(42); currentComponent = second; incrementGeneration(); wrapper.setProps({ update: 'now' }); expect(first.unmounted).toHaveBeenCalledTimes(0); expect(second.mounted).toHaveBeenCalledTimes(0); expect(wrapper.text()).toContain(43); }); describe('should assmeble props for nested children', () => { const testSuite = () => { const RenderChildren = ({ children }) =>
    {children}
    ; const RenderProp = jest.fn().mockImplementation(({ prop }) =>
    {prop}
    ); const DefaultProp = jest.fn().mockImplementation(({ prop }) => (
    {prop ? (
    42
    ) : (
    24
    )}
    )); DefaultProp.defaultProps = { prop: 'defaultValue', }; const App = () => (
    ); logger.warn.mockClear(); const suite = () => (
    ); const wrapper = TestRenderer.create(suite()); incrementGeneration(); wrapper.update(suite()); return { RenderProp, DefaultProp }; }; it('for Component SFC', () => { const { RenderProp, DefaultProp } = testSuite(); const Comp = () =>
    ; expect(.type.prototype.render).not.toBeDefined(); expect(RenderProp).toHaveBeenCalledTimes(6); expect(RenderProp.mock.calls[0][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[1][0]).toEqual({ value: 24 }); expect(RenderProp.mock.calls[2][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[3][0]).toEqual({ value: 24 }); expect(DefaultProp).toHaveBeenCalledTimes(3); expect(DefaultProp.mock.calls[0][0]).toEqual({ prop: 'defaultValue' }); expect(DefaultProp.mock.calls[1][0]).toEqual({ prop: 'defaultValue' }); expect(logger.warn).not.toHaveBeenCalled(); }); // unstable between React15 / 16.6 it.skip('for Pure SFC', () => { configuration.pureSFC = true; const { RenderProp, DefaultProp } = testSuite(); const Comp = () =>
    ; expect(.type.prototype.render).not.toBeDefined(); configuration.pureSFC = false; expect(RenderProp).toHaveBeenCalledTimes(6); expect(RenderProp.mock.calls[0][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[1][0]).toEqual({ value: 24 }); expect(RenderProp.mock.calls[2][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[3][0]).toEqual({ value: 24 }); expect(DefaultProp).toHaveBeenCalledTimes(3); expect(DefaultProp.mock.calls[0][0]).toEqual({ prop: 'defaultValue' }); expect(DefaultProp.mock.calls[1][0]).toEqual({ prop: 'defaultValue' }); expect(logger.warn).not.toHaveBeenCalled(); }); it('for SFC disabled', () => { configuration.allowSFC = false; const { RenderProp, DefaultProp } = testSuite(); const Comp = () =>
    ; expect(.type.prototype.render).toBeDefined(); configuration.allowSFC = true; expect(RenderProp).toHaveBeenCalledTimes(6); expect(RenderProp.mock.calls[0][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[1][0]).toEqual({ value: 24 }); expect(RenderProp.mock.calls[2][0]).toEqual({ value: 42 }); expect(RenderProp.mock.calls[3][0]).toEqual({ value: 24 }); expect(DefaultProp).toHaveBeenCalledTimes(3); expect(DefaultProp.mock.calls[0][0]).toEqual({ prop: 'defaultValue' }); expect(DefaultProp.mock.calls[1][0]).toEqual({ prop: 'defaultValue' }); expect(logger.warn).not.toHaveBeenCalled(); }); }); describe('when an error occurs in render', () => { beforeEach(() => { jest.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { console.error.mockRestore(); }); it('should catch error to the boundary', () => { if (!React.Suspense) { // this test is unstable on React 15 expect(true).toBe(true); return; } configureGeneration(1, 1); const App = () =>
    Normal application
    ; reactHotLoader.register(App, 'App', 'test.js'); const TestCase = () => ( ); const wrapper = mount(); { const errorFn = active => { if (active) throw new Error(); return null; }; const App = ({ active }) => (
    Normal application {errorFn(active)}
    ); reactHotLoader.register(App, 'App', 'test.js'); expect(() => wrapper.setProps({ children: })).not.toThrow(); } expect(logger.warn).toHaveBeenCalledWith( `React-hot-loader: run time error during reconciliation`, expect.any(Error), ); }); it('should catch error', () => { const App = () =>
    Normal application
    ; reactHotLoader.register(App, 'App', 'test.js'); const TestCase = () => ( ); mount(); { const errorFn = active => { if (active) throw new Error(); return null; }; const App = ({ active }) => (
    Normal application {errorFn(active)}
    ); reactHotLoader.register(App, 'App', 'test.js'); closeGeneration(); // Not throwing anymore ? // expect(() => wrapper.setProps({ children: })).toThrow(); expect(internalConfiguration.disableProxyCreation).toBe(false); } expect(logger.warn).toHaveBeenCalledWith( `React-hot-loader: run time error during reconciliation`, expect.any(Error), ); }); it('should catch "suspense" error, but swallow it', () => { const App = () =>
    Normal application
    ; reactHotLoader.register(App, 'App', 'test.js'); const TestCase = () => ( ); // const wrapper = mount(); mount(); { const errorFn = active => { if (active) throw Promise.resolve(); return null; }; const App = ({ active }) => (
    Normal application {errorFn(active)}
    ); reactHotLoader.register(App, 'App', 'test.js'); closeGeneration(); // Not throwing anymore ? // expect(() => wrapper.setProps({ children: })).toThrow(); expect(internalConfiguration.disableProxyCreation).toBe(false); } // not stable across es5/modern build modes. Tested manually // expect(logger.warn).not.toHaveBeenCalled(); }); }); }); }); ================================================ FILE: test/utils.test.js ================================================ import React, { Component } from 'react'; import reactHotLoader from '../src/reactHotLoader'; import { areComponentsEqual, cold } from '../src/utils.dev'; import configuration from '../src/configuration'; import logger from '../src/logger'; reactHotLoader.patch(React); jest.mock('../src/logger'); describe('utils (dev)', () => { describe('areComponentsEqual', () => { const createClasses = () => { class Component1 extends Component { render() { return 42; } } class Component2 extends Component { render() { return 43; } } return { Component1, Component2 }; }; const createStateless = () => { const Component1 = () => 42; const Component2 = () => 43; return { Component1, Component2 }; }; const testSuite = factory => { it('should compare non-registred components', () => { const { Component1, Component2 } = factory(); const element1 = ; const element2 = ; expect(Component1 === Component2).toBe(false); expect(Component1 === element1.type).toBe(false); expect(areComponentsEqual(Component1, element1.type)).toBe(true); expect(areComponentsEqual(Component1, element2.type)).toBe(false); }); it('should compare registered components', () => { const { Component1, Component2 } = factory(); reactHotLoader.register(Component1, 'Class1', 'util.dev'); reactHotLoader.register(Component2, 'Class2', 'util.dev'); const element1 = ; const element2 = ; expect(Component1 === Component2).toBe(false); expect(Component1 === element1.type).toBe(false); expect(areComponentsEqual(Component1, element1.type)).toBe(true); expect(areComponentsEqual(Component1, element2.type)).toBe(false); }); }; describe('class based', () => testSuite(createClasses)); describe('function based', () => testSuite(createStateless)); }); describe('cold', () => { it('should disable RHL for "cold" Components', () => { const Component1 = () =>
    42
    ; const Component2 = () =>
    42
    ; reactHotLoader.register(Component1, 'Class1', 'util.dev'); reactHotLoader.register(Component2, 'Class2', 'util.dev'); cold(Component1); const element1 = ; const element2 = ; expect(Component1 === element1.type).toBe(true); expect(Component2 === element2.type).toBe(false); expect(areComponentsEqual(Component1, element1.type)).toBe(true); expect(areComponentsEqual(Component2, element2.type)).toBe(true); }); it('should components by fileName', () => { const Component1 = () =>
    42
    ; const Component2 = () =>
    42
    ; configuration.onComponentRegister = (type, name, file) => { if (file === 'oneFile') cold(type); }; reactHotLoader.register(Component1, 'Class1', 'oneFile'); reactHotLoader.register(Component2, 'Class2', 'anotherFile'); const element1 = ; const element2 = ; expect(Component1 === element1.type).toBe(true); expect(Component2 === element2.type).toBe(false); expect(areComponentsEqual(Component1, element1.type)).toBe(true); expect(areComponentsEqual(Component2, element2.type)).toBe(true); }); it('should report on cold update', () => { const Component1 = () =>
    42
    ; const Component2 = () =>
    42
    ; cold(Component1); // cold(Component2) reactHotLoader.register(Component1, 'Cold', 'Winter'); reactHotLoader.register(Component1, 'Cold', 'Winter'); // first registration is expected expect(logger.error).not.toHaveBeenCalled(); reactHotLoader.register(Component2, 'Cold', 'Winter'); expect(logger.error).toHaveBeenCalledWith( `React-hot-loader: Cold component`, 'Cold', 'at', 'Winter', 'has been updated', ); }); }); }); ================================================ FILE: testConfig/babel.js ================================================ const { createTransformer } = require('babel-jest'); const path = require('path'); const TARGET_ES2015 = 'es2015'; const TARGET_MODERN = 'modern'; const TARGETS = [TARGET_ES2015, TARGET_MODERN]; const getOptions = target => { switch (target) { case TARGET_ES2015: return { babelrc: false, presets: ['env', 'react'], plugins: ['transform-class-properties', 'transform-object-rest-spread'], }; case TARGET_MODERN: return { babelrc: false, presets: [ [ 'env', { targets: { browsers: 'chrome 60', }, }, ], 'react', ], plugins: ['transform-class-properties', 'transform-object-rest-spread'], }; default: throw new Error('You must specify a BABEL_TARGET: es2015 or modern'); } }; module.exports = createTransformer(getOptions(process.env.BABEL_TARGET)); module.exports.getOptions = getOptions; module.exports.TARGETS = TARGETS; ================================================ FILE: testConfig/setupTests.js ================================================ /* eslint-disable global-require */ import React from 'react'; import Enzyme from 'enzyme'; function getAdapter() { if (React.version.startsWith('15')) { return require('enzyme-adapter-react-15'); } else if (React.version.startsWith('16')) { return require('enzyme-adapter-react-16'); } else if (React.version.startsWith('17')) { return require('@wojtekmaj/enzyme-adapter-react-17'); } throw new Error('this version of React is not supported by Enzyme'); } const Adapter = getAdapter(); Enzyme.configure({ adapter: new Adapter() }); ================================================ FILE: webpack.js ================================================ if (process.env.NODE_ENV === 'production') { module.exports = require('./dist/webpack.production.min.js'); } else { module.exports = require('./dist/webpack.development.js'); }