-
Notifications
You must be signed in to change notification settings - Fork 799
/
Copy pathhot.dev.js
180 lines (159 loc) · 6.05 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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 (
<AppContainer {...props}>
<WrappedComponent {...this.props} />
</AppContainer>
);
}
},
);
};
};
export default hot;