1 | const hmrPrefix = 'HMR:';
|
2 | const log = {
|
3 | info: (message) => console.info(`${hmrPrefix} ${message}`),
|
4 | warn: (message) => console.warn(`${hmrPrefix} ${message}`),
|
5 | error: (message) => console.error(`${hmrPrefix} ${message}`),
|
6 | };
|
7 | const refresh = 'Application needs to be restarted in order to apply the changes.';
|
8 | const hotOptions = {
|
9 | ignoreUnaccepted: false,
|
10 | ignoreDeclined: false,
|
11 | ignoreErrored: false,
|
12 | onUnaccepted(data) {
|
13 | const chain = [].concat(data.chain);
|
14 | const last = chain[chain.length - 1];
|
15 |
|
16 | if (last === 0) {
|
17 | chain.pop();
|
18 | }
|
19 |
|
20 | log.warn(`Ignored an update to unaccepted module: `);
|
21 | chain.forEach(mod => log.warn(` ➭ ${mod}`));
|
22 | },
|
23 | onDeclined(data) {
|
24 | log.warn(`Ignored an update to declined module:`);
|
25 | data.chain.forEach(mod => log.warn(` ➭ ${mod}`));
|
26 | },
|
27 | onErrored(data) {
|
28 | log.warn(
|
29 | `Ignored an error while updating module ${data.moduleId} <${data.type}>`
|
30 | );
|
31 | log.warn(data.error);
|
32 | },
|
33 | };
|
34 |
|
35 | let nextHash;
|
36 | let currentHash;
|
37 |
|
38 | function upToDate() {
|
39 | return nextHash.indexOf(__webpack_hash__) >= 0;
|
40 | }
|
41 |
|
42 | function result(modules, appliedModules) {
|
43 | const unaccepted = modules.filter(
|
44 | (moduleId) => appliedModules && appliedModules.indexOf(moduleId) < 0
|
45 | );
|
46 |
|
47 | if (unaccepted.length > 0) {
|
48 | log.warn('The following modules could not be updated:');
|
49 |
|
50 | for (const moduleId of unaccepted) {
|
51 | log.warn(` ⦻ ${moduleId}`);
|
52 | }
|
53 | }
|
54 |
|
55 | if (!(appliedModules || []).length) {
|
56 | log.info('No Modules Updated.');
|
57 | } else {
|
58 | log.info('The following modules were updated:');
|
59 |
|
60 | for (const moduleId of appliedModules) {
|
61 | log.info(` ↻ ${moduleId}`);
|
62 | }
|
63 |
|
64 | const numberIds = appliedModules.every(
|
65 | (moduleId) => typeof moduleId === 'number'
|
66 | );
|
67 | if (numberIds) {
|
68 | log.info(
|
69 | 'Please consider using the NamedModulesPlugin for module names.'
|
70 | );
|
71 | }
|
72 | }
|
73 | }
|
74 |
|
75 | function check(options) {
|
76 | return module.hot
|
77 | .check()
|
78 | .then((modules) => {
|
79 | if (!modules) {
|
80 | log.warn(
|
81 | `Cannot find update. ${refresh}`
|
82 | );
|
83 | return null;
|
84 | }
|
85 |
|
86 | return module.hot
|
87 | .apply(hotOptions)
|
88 | .then((appliedModules) => {
|
89 | let nextCheck;
|
90 | if (!upToDate()) {
|
91 | nextCheck = check(options);
|
92 | }
|
93 |
|
94 | result(modules, appliedModules);
|
95 |
|
96 | if (upToDate()) {
|
97 |
|
98 | log.info(`Successfully applied update with hmr hash ${currentHash}. App is up to date.`);
|
99 | }
|
100 |
|
101 | return nextCheck || null;
|
102 | })
|
103 | .catch((err) => {
|
104 | const status = module.hot.status();
|
105 | if (['abort', 'fail'].indexOf(status) >= 0) {
|
106 |
|
107 | log.error(`Cannot apply update with hmr hash ${currentHash}.`);
|
108 | log.error(err.message || err.stack);
|
109 | } else {
|
110 | log.error(`Update failed: ${err.message || err.stack}`);
|
111 | }
|
112 | });
|
113 | })
|
114 | .catch((err) => {
|
115 | const status = module.hot.status();
|
116 | if (['abort', 'fail'].indexOf(status) >= 0) {
|
117 | log.error(`Cannot check for update. ${refresh}`);
|
118 | log.error(err.message || err.stack);
|
119 | } else {
|
120 | log.error(`Update check failed: ${err.message || err.stack}`);
|
121 | }
|
122 | });
|
123 | }
|
124 |
|
125 | if (module.hot) {
|
126 | log.info('Hot Module Replacement Enabled. Waiting for signal.');
|
127 | } else {
|
128 | log.error('Hot Module Replacement is disabled.');
|
129 | }
|
130 |
|
131 | function update(latestHash, options) {
|
132 | nextHash = latestHash;
|
133 | if (!upToDate()) {
|
134 | const status = module.hot.status();
|
135 |
|
136 | if (status === 'idle') {
|
137 |
|
138 | log.info(`Checking for updates to the bundle with hmr hash ${currentHash}.`);
|
139 | return check(options);
|
140 | } else if (['abort', 'fail'].indexOf(status) >= 0) {
|
141 | log.warn(
|
142 | `Cannot apply update. A previous update ${status}ed. ${refresh}`
|
143 | );
|
144 | }
|
145 | }
|
146 | };
|
147 |
|
148 | function getNextHash(hash, getFileContent) {
|
149 | const file = getFileContent(`${hash}.hot-update.json`);
|
150 | return file.readText().then(hotUpdateContent => {
|
151 | if (hotUpdateContent) {
|
152 | const manifest = JSON.parse(hotUpdateContent);
|
153 | const newHash = manifest.h;
|
154 | return getNextHash(newHash, getFileContent);
|
155 | } else {
|
156 | return Promise.resolve(hash);
|
157 | }
|
158 | }).catch(error => Promise.reject(error));
|
159 | }
|
160 |
|
161 | module.exports = function checkState(initialHash, getFileContent) {
|
162 | currentHash = initialHash;
|
163 | return getNextHash(initialHash, getFileContent).then(nextHash => {
|
164 | if (nextHash != initialHash) {
|
165 | return update(nextHash, {});
|
166 | }
|
167 | })
|
168 | }
|