Skip to content

Commit

Permalink
Merge pull request #1165 from gaearon/january-fixes
Browse files Browse the repository at this point in the history
January fixes
  • Loading branch information
theKashey authored Jan 30, 2019
2 parents 3a429d4 + 4b02767 commit 3608356
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 304 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,28 @@ setConfig({

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`. **!!**
Expand Down
15 changes: 15 additions & 0 deletions examples/all-possible-containers/src/components/LazyComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'
import { EDIT_ME } from './_editMe'
import Counter from './Counter'

const LazyComponent = () => (
<div>
<fieldset>
<legend>Lazy Component</legend>
{EDIT_ME}
<Counter />
</fieldset>
</div>
)

export default LazyComponent
30 changes: 16 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
'use strict'

var evalAllowed = false;
try {
eval('evalAllowed = true');
} catch (e) {
// eval not allowed due to CSP
}
if (!module.hot || process.env.NODE_ENV === 'production') {
module.exports = require('./dist/react-hot-loader.production.min.js');
} else {
var evalAllowed = false;
try {
eval('evalAllowed = true');
} catch (e) {
// eval not allowed due to CSP
}

// RHL needs setPrototypeOf to operate Component inheritance, and eval to patch methods
var platformSupported = !!Object.setPrototypeOf && evalAllowed;
// RHL needs setPrototypeOf to operate Component inheritance, and eval to patch methods
var jsFeaturesPresent = typeof window !== 'undefined' && !!Object.setPrototypeOf;

if (!module.hot || process.env.NODE_ENV === 'production' || !platformSupported) {
if (module.hot) {
if (!jsFeaturesPresent && evalAllowed) {
// we are not in prod mode, but RHL could not be activated
console.warn('React-Hot-Loaded is not supported in this environment');
console.warn('React-Hot-Loader is not supported in this environment');
module.exports = require('./dist/react-hot-loader.production.min.js');
} else {
module.exports = window.reactHotLoaderGlobal = require('./dist/react-hot-loader.development.js');
}
module.exports = require('./dist/react-hot-loader.production.min.js');
} else {
module.exports = require('./dist/react-hot-loader.development.js');
}
9 changes: 7 additions & 2 deletions src/AppContainer.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import defaultPolyfill, { polyfill } from 'react-lifecycles-compat'
import logger from './logger'
import { get as getGeneration } from './global/generation'
import { get as getGeneration, hotComparisonOpen } from './global/generation'
import configuration from './configuration'
import { EmptyErrorPlaceholder, logException } from './errorReporter'

Expand Down Expand Up @@ -38,9 +38,14 @@ class AppContainer extends React.Component {

componentDidCatch(error, errorInfo) {
logger.error(error)

if (!hotComparisonOpen()) {
// do not log error outside of HMR cycle
return
}
const { errorReporter = configuration.errorReporter } = this.props
if (!errorReporter) {
logException(error, errorInfo)
logException(error, errorInfo, this)
}
this.setState({
error,
Expand Down
36 changes: 25 additions & 11 deletions src/babel.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const shouldIgnoreFile = file =>
.match(/node_modules\/(react|react-hot-loader)([\/]|$)/)
/* eslint-enable */

module.exports = function plugin(args) {
module.exports = 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(
Expand All @@ -27,34 +27,42 @@ module.exports = function plugin(args) {
}
const { types: t, template } = args

const { safetyNet = true } = options

const buildRegistration = template(
'reactHotLoader.register(ID, NAME, FILENAME);',
templateOptions,
)
const headerTemplate = template(
`(function () {
var enterModule = require('react-hot-loader').enterModule;
var enterModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).enterModule;
enterModule && enterModule(module);
}())`,
templateOptions,
)
const footerTemplate = template(
`(function () {
var leaveModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).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 = require('react-hot-loader').default;
var leaveModule = require('react-hot-loader').leaveModule;

const buildTagger = template(
`
(function () {
var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ?reactHotLoaderGlobal : require('react-hot-loader')).default;
if (!reactHotLoader) {
return;
}
REGISTRATIONS
leaveModule(module);
REGISTRATIONS
}());
`,
templateOptions,
Expand Down Expand Up @@ -151,12 +159,18 @@ module.exports = function plugin(args) {
registrations.length &&
!shouldIgnoreFile(file.opts.filename)
) {
node.body.unshift(headerTemplate())
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())
}
}
},
},
Expand Down
26 changes: 21 additions & 5 deletions src/errorReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React from 'react'
import ReactDom from 'react-dom'

import configuration from './configuration'
import { getComponentDisplayName } from './internal/reactUtils'

let lastError = []

Expand Down Expand Up @@ -33,15 +34,24 @@ const inlineErrorStyle = {

const listStyle = {}

export const EmptyErrorPlaceholder = () => (
export const EmptyErrorPlaceholder = ({ component }) => (
<span style={inlineErrorStyle} role="img" aria-label="Rect-Hot-Loader Error">
⚛️🔥🤕
⚛️🔥🤕 ({component
? getComponentDisplayName(component.constructor || component)
: 'Unknown location'})
</span>
)

const mapError = ({ error, errorInfo }) => (
const mapError = ({ error, errorInfo, component }) => (
<div>
<p style={{ color: 'red' }}>
{component && (
<span>
({component
? getComponentDisplayName(component.constructor || component)
: 'Unknown location'})
</span>
)}
{error.toString ? error.toString() : error.message || 'undefined error'}
</p>
{errorInfo && errorInfo.componentStack ? (
Expand Down Expand Up @@ -129,7 +139,13 @@ export const clearExceptions = () => {
}
}

export const logException = (error, errorInfo) => {
lastError.push({ error, errorInfo })
export const logException = (error, errorInfo, component) => {
// do not suppress error

/* eslint-disable no-console */
console.error(error)
/* eslint-enable */

lastError.push({ error, errorInfo, component })
initErrorOverlay()
}
1 change: 1 addition & 0 deletions src/internal/getReactStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const markUpdate = ({ fiber }) => {
fiber.expirationTime = 1
if (fiber.alternate) {
fiber.alternate.expirationTime = 1
fiber.alternate.type = fiber.type
}
fiber.memoizedProps = Object.assign(
{ cacheBusterProp: true },
Expand Down
7 changes: 3 additions & 4 deletions src/proxy/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,17 @@ export const doesSupportClasses = (function() {
}
})()

const ES6ProxyComponentFactory =
doesSupportClasses &&
const ES6ProxyComponentFactory = (InitialParent, postConstructionAction) =>
indirectEval(`
(function(InitialParent, postConstructionAction) {
return class ProxyComponent extends InitialParent {
return class ${InitialParent.name || 'HotComponent'} extends InitialParent {
constructor(props, context) {
super(props, context)
postConstructionAction.call(this)
}
}
})
`)
`)(InitialParent, postConstructionAction)

const ES5ProxyComponentFactory = function(
InitialParent,
Expand Down
1 change: 1 addition & 0 deletions src/reconciler/fiberUpdater.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const lazyConstructor = '_ctor'
export const updateLazy = (target, type) => {
const ctor = type[lazyConstructor]
if (target[lazyConstructor] !== type[lazyConstructor]) {
// just execute `import` and RHL.register will do the job
ctor()
}
if (!target[lazyConstructor].isPatchedByReactHotLoader) {
Expand Down
4 changes: 2 additions & 2 deletions src/reconciler/proxyAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function componentRender() {
if (error && generation === getGeneration()) {
return React.createElement(
configuration.errorReporter || EmptyErrorPlaceholder,
{ error, errorInfo },
{ error, errorInfo, component: this },
)
}
try {
Expand All @@ -99,7 +99,7 @@ function componentRender() {
generation: getGeneration(),
}
if (!configuration.errorReporter) {
logException(renderError)
logException(renderError, undefined, this)
}
return componentRender.call(this)
}
Expand Down
Loading

0 comments on commit 3608356

Please sign in to comment.