/
hot.dev.js
132 lines (117 loc) · 4.47 KB
/
hot.dev.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import React, { Component } from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';
import { getComponentDisplayName } from './internal/reactUtils';
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';
/* 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 makeHotExport = (sourceModule, moduleId) => {
const updateInstances = possibleError => {
if (possibleError && possibleError instanceof Error) {
console.error(possibleError);
return;
}
const module = hotModule(moduleId);
clearTimeout(module.updateTimeout);
module.updateTimeout = setTimeout(() => {
try {
requireIndirect(moduleId);
} catch (e) {
console.error('React-Hot-Loader: error detected while loading', moduleId);
console.error(e);
}
module.instances.forEach(inst => inst.forceUpdate());
});
};
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` prodived', 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 (
<AppContainer {...props}>
<WrappedComponent {...this.props} />
</AppContainer>
);
}
},
);
};
};
export default hot;