diff --git a/examples/styled-components/src/App.js b/examples/styled-components/src/App.js index 0c28c5ee1..d76d87737 100644 --- a/examples/styled-components/src/App.js +++ b/examples/styled-components/src/App.js @@ -25,7 +25,7 @@ const indirect = { const indirectStyled = { DS: styled.div` - border: px solid #f00; + border: 2px solid #f00; `, DE: emoStyled('div')`border: 1px solid #F00`, }; @@ -39,10 +39,17 @@ const OtherComponent = () => test; const Context = React.createContext(); const Hook = () => { - const [state] = React.useState({ x: 4 }); + const [state, setState] = React.useState({ x: 2 }); + React.useEffect( + () => + setState(state => ({ + x: state.x + 1, + })), + [], + ); return (
- hook state: {state.x} + hook state 1: {state.x}
); @@ -128,6 +135,7 @@ const App = () => ; setConfig({ logLevel: 'debug', + hotHooks: true, }); // return App; diff --git a/examples/styled-components/webpack.config.babel.js b/examples/styled-components/webpack.config.babel.js index b66bdf995..86f5ba2b1 100644 --- a/examples/styled-components/webpack.config.babel.js +++ b/examples/styled-components/webpack.config.babel.js @@ -6,7 +6,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/index'], mode: process.env.NODE_ENV || 'development', - devtool: 'eval-source-map', + devtool: false, output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', diff --git a/index.js b/index.js index 2ac074c13..98dde5a8a 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,9 @@ if (process.env.NODE_ENV === 'production') { // this is just server environment module.exports = require('./dist/react-hot-loader.production.min.js'); } else if (!module.hot) { - console.error('React-Hot-Loader: Hot Module Replacement is not enabled'); module.exports = require('./dist/react-hot-loader.production.min.js'); + module.exports.AppContainer.warnAboutHMRDisabled = true; + module.exports.hot.shouldWrapWithAppContainer = true; } else { var evalAllowed = false; try { diff --git a/root.js b/root.js index 6f4cec538..65b702932 100644 --- a/root.js +++ b/root.js @@ -1,28 +1,30 @@ -if (module.hot) { +if (process.env.NODE_ENV !== 'production') { var hot = require('./index').hot; - var cache = require.cache; + 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. ' + + 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. ' + + ); + } + // 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]; } - // remove self from a cache - delete cache[module.id]; // setup hot for caller exports.hot = hot(parent); } else { // prod mode - exports.hot = function(a) { + exports.hot = function (a) { return a; }; } diff --git a/src/AppContainer.prod.js b/src/AppContainer.prod.js index 1bb8acc91..c797f616d 100644 --- a/src/AppContainer.prod.js +++ b/src/AppContainer.prod.js @@ -3,7 +3,16 @@ import React from 'react'; function AppContainer(props) { + if (AppContainer.warnAboutHMRDisabled) { + AppContainer.warnAboutHMRDisabled = true; + console.error( + 'React-Hot-Loader: misconfiguration detected, using production version in not 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; diff --git a/src/babel.prod.js b/src/babel.prod.js index 42681f209..5b6166fb2 100644 --- a/src/babel.prod.js +++ b/src/babel.prod.js @@ -1,4 +1,6 @@ const RHLPackage = 'react-hot-loader'; +const RHLRootPackage = 'react-hot-loader/root'; +const RHLPackages = [RHLPackage, RHLRootPackage]; function isImportedFromRHL(path, name) { const binding = path.scope.getBinding(name); @@ -6,7 +8,7 @@ function isImportedFromRHL(path, name) { if (bindingType === 'ImportSpecifier' || bindingType === 'ImportNamespaceSpecifier') { const bindingParent = binding.path.parent; - return bindingParent.source.value === RHLPackage; + return RHLPackages.includes(bindingParent.source.value); } return false; @@ -20,7 +22,7 @@ function getRHLContext(file) { const bodyItem = body[i]; const { source, specifiers } = bodyItem; - if (bodyItem.type !== 'ImportDeclaration' || source.value !== RHLPackage) { + if (bodyItem.type !== 'ImportDeclaration' || !RHLPackages.includes(source.value)) { continue; } @@ -58,6 +60,7 @@ export default function plugin() { 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 @@ -69,7 +72,21 @@ export default function plugin() { 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 + isImportedFromRHL(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' && diff --git a/src/hot.prod.js b/src/hot.prod.js index 7f8a68356..fd1e64c48 100644 --- a/src/hot.prod.js +++ b/src/hot.prod.js @@ -1 +1,17 @@ -export default () => Component => Component; +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; diff --git a/test/__babel_fixtures__/drop-hot.prod.js b/test/__babel_fixtures__/drop-hot.prod.js index 13a729add..486487916 100644 --- a/test/__babel_fixtures__/drop-hot.prod.js +++ b/test/__babel_fixtures__/drop-hot.prod.js @@ -1,6 +1,7 @@ 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' @@ -10,6 +11,7 @@ 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); @@ -21,4 +23,4 @@ namedFoo(module)(App); RHL.foo(module)(App); NOTRHL.hot(module)(App); -export { a, b, c, d, e }; +export { a, b, c, d, e, z }; diff --git a/test/__snapshots__/babel.test.js.snap b/test/__snapshots__/babel.test.js.snap index b46f621d6..05f476cb2 100644 --- a/test/__snapshots__/babel.test.js.snap +++ b/test/__snapshots__/babel.test.js.snap @@ -1007,7 +1007,7 @@ exports[`babel Targetting "es2015" tags potential React components drop hot.prod Object.defineProperty(exports, \\"__esModule\\", { value: true }); -exports.e = exports.d = exports.c = exports.b = exports.a = undefined; +exports.z = exports.e = exports.d = exports.c = exports.b = exports.a = undefined; var _react = require('react'); @@ -1019,6 +1019,8 @@ 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); @@ -1036,6 +1038,7 @@ var App = function App() { }; var a = App; +var z = App; var b = App; var c = App; var d = App; @@ -1051,7 +1054,8 @@ exports.a = a; exports.b = b; exports.c = c; exports.d = d; -exports.e = e;" +exports.e = e; +exports.z = z;" `; exports[`babel Targetting "es2015" tags potential React components issue 246.js 1`] = ` @@ -1883,7 +1887,7 @@ exports[`babel Targetting "modern" tags potential React components drop hot.prod Object.defineProperty(exports, \\"__esModule\\", { value: true }); -exports.e = exports.d = exports.c = exports.b = exports.a = undefined; +exports.z = exports.e = exports.d = exports.c = exports.b = exports.a = undefined; var _react = require('react'); @@ -1895,6 +1899,8 @@ 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); @@ -1910,6 +1916,7 @@ const App = () => _react2.default.createElement( ); const a = App; +const z = App; const b = App; const c = App; const d = App; @@ -1925,7 +1932,8 @@ exports.a = a; exports.b = b; exports.c = c; exports.d = d; -exports.e = e;" +exports.e = e; +exports.z = z;" `; exports[`babel Targetting "modern" tags potential React components issue 246.js 1`] = `