client.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*eslint-env browser*/
  2. /*global __resourceQuery __webpack_public_path__*/
  3. var options = {
  4. path: '/__webpack_hmr',
  5. timeout: 20 * 1000,
  6. overlay: true,
  7. reload: false,
  8. log: true,
  9. warn: true,
  10. name: '',
  11. autoConnect: true,
  12. overlayStyles: {},
  13. overlayWarnings: false,
  14. ansiColors: {},
  15. };
  16. if (__resourceQuery) {
  17. var overrides = Object.fromEntries(
  18. new URLSearchParams(__resourceQuery.slice(1))
  19. );
  20. setOverrides(overrides);
  21. }
  22. if (typeof window === 'undefined') {
  23. // do nothing
  24. } else if (typeof window.EventSource === 'undefined') {
  25. console.warn(
  26. "webpack-hot-middleware's client requires EventSource to work. " +
  27. 'You should include a polyfill if you want to support this browser: ' +
  28. 'https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events#Tools'
  29. );
  30. } else {
  31. if (options.autoConnect) {
  32. connect();
  33. }
  34. }
  35. /* istanbul ignore next */
  36. function setOptionsAndConnect(overrides) {
  37. setOverrides(overrides);
  38. connect();
  39. }
  40. function setOverrides(overrides) {
  41. if (overrides.autoConnect)
  42. options.autoConnect = overrides.autoConnect == 'true';
  43. if (overrides.path) options.path = overrides.path;
  44. if (overrides.timeout) options.timeout = overrides.timeout;
  45. if (overrides.overlay) options.overlay = overrides.overlay !== 'false';
  46. if (overrides.reload) options.reload = overrides.reload !== 'false';
  47. if (overrides.noInfo && overrides.noInfo !== 'false') {
  48. options.log = false;
  49. }
  50. if (overrides.name) {
  51. options.name = overrides.name;
  52. }
  53. if (overrides.quiet && overrides.quiet !== 'false') {
  54. options.log = false;
  55. options.warn = false;
  56. }
  57. if (overrides.dynamicPublicPath) {
  58. options.path = __webpack_public_path__ + options.path;
  59. }
  60. if (overrides.ansiColors)
  61. options.ansiColors = JSON.parse(overrides.ansiColors);
  62. if (overrides.overlayStyles)
  63. options.overlayStyles = JSON.parse(overrides.overlayStyles);
  64. if (overrides.overlayWarnings) {
  65. options.overlayWarnings = overrides.overlayWarnings == 'true';
  66. }
  67. }
  68. function EventSourceWrapper() {
  69. var source;
  70. var lastActivity = new Date();
  71. var listeners = [];
  72. init();
  73. var timer = setInterval(function () {
  74. if (new Date() - lastActivity > options.timeout) {
  75. handleDisconnect();
  76. }
  77. }, options.timeout / 2);
  78. function init() {
  79. source = new window.EventSource(options.path);
  80. source.onopen = handleOnline;
  81. source.onerror = handleDisconnect;
  82. source.onmessage = handleMessage;
  83. }
  84. function handleOnline() {
  85. if (options.log) console.log('[HMR] connected');
  86. lastActivity = new Date();
  87. }
  88. function handleMessage(event) {
  89. lastActivity = new Date();
  90. for (var i = 0; i < listeners.length; i++) {
  91. listeners[i](event);
  92. }
  93. }
  94. function handleDisconnect() {
  95. clearInterval(timer);
  96. source.close();
  97. setTimeout(init, options.timeout);
  98. }
  99. return {
  100. addMessageListener: function (fn) {
  101. listeners.push(fn);
  102. },
  103. };
  104. }
  105. function getEventSourceWrapper() {
  106. if (!window.__whmEventSourceWrapper) {
  107. window.__whmEventSourceWrapper = {};
  108. }
  109. if (!window.__whmEventSourceWrapper[options.path]) {
  110. // cache the wrapper for other entries loaded on
  111. // the same page with the same options.path
  112. window.__whmEventSourceWrapper[options.path] = EventSourceWrapper();
  113. }
  114. return window.__whmEventSourceWrapper[options.path];
  115. }
  116. function connect() {
  117. getEventSourceWrapper().addMessageListener(handleMessage);
  118. function handleMessage(event) {
  119. if (event.data == '\uD83D\uDC93') {
  120. return;
  121. }
  122. try {
  123. processMessage(JSON.parse(event.data));
  124. } catch (ex) {
  125. if (options.warn) {
  126. console.warn('Invalid HMR message: ' + event.data + '\n' + ex);
  127. }
  128. }
  129. }
  130. }
  131. // the reporter needs to be a singleton on the page
  132. // in case the client is being used by multiple bundles
  133. // we only want to report once.
  134. // all the errors will go to all clients
  135. var singletonKey = '__webpack_hot_middleware_reporter__';
  136. var reporter;
  137. if (typeof window !== 'undefined') {
  138. if (!window[singletonKey]) {
  139. window[singletonKey] = createReporter();
  140. }
  141. reporter = window[singletonKey];
  142. }
  143. function createReporter() {
  144. var strip = require('strip-ansi');
  145. var overlay;
  146. if (typeof document !== 'undefined' && options.overlay) {
  147. overlay = require('./client-overlay')({
  148. ansiColors: options.ansiColors,
  149. overlayStyles: options.overlayStyles,
  150. });
  151. }
  152. var styles = {
  153. errors: 'color: #ff0000;',
  154. warnings: 'color: #999933;',
  155. };
  156. var previousProblems = null;
  157. function log(type, obj) {
  158. var newProblems = obj[type]
  159. .map(function (msg) {
  160. return strip(msg);
  161. })
  162. .join('\n');
  163. if (previousProblems == newProblems) {
  164. return;
  165. } else {
  166. previousProblems = newProblems;
  167. }
  168. var style = styles[type];
  169. var name = obj.name ? "'" + obj.name + "' " : '';
  170. var title = '[HMR] bundle ' + name + 'has ' + obj[type].length + ' ' + type;
  171. // NOTE: console.warn or console.error will print the stack trace
  172. // which isn't helpful here, so using console.log to escape it.
  173. if (console.group && console.groupEnd) {
  174. console.group('%c' + title, style);
  175. console.log('%c' + newProblems, style);
  176. console.groupEnd();
  177. } else {
  178. console.log(
  179. '%c' + title + '\n\t%c' + newProblems.replace(/\n/g, '\n\t'),
  180. style + 'font-weight: bold;',
  181. style + 'font-weight: normal;'
  182. );
  183. }
  184. }
  185. return {
  186. cleanProblemsCache: function () {
  187. previousProblems = null;
  188. },
  189. problems: function (type, obj) {
  190. if (options.warn) {
  191. log(type, obj);
  192. }
  193. if (overlay) {
  194. if (options.overlayWarnings || type === 'errors') {
  195. overlay.showProblems(type, obj[type]);
  196. return false;
  197. }
  198. overlay.clear();
  199. }
  200. return true;
  201. },
  202. success: function () {
  203. if (overlay) overlay.clear();
  204. },
  205. useCustomOverlay: function (customOverlay) {
  206. overlay = customOverlay;
  207. },
  208. };
  209. }
  210. var processUpdate = require('./process-update');
  211. var customHandler;
  212. var subscribeAllHandler;
  213. function processMessage(obj) {
  214. switch (obj.action) {
  215. case 'building':
  216. if (options.log) {
  217. console.log(
  218. '[HMR] bundle ' +
  219. (obj.name ? "'" + obj.name + "' " : '') +
  220. 'rebuilding'
  221. );
  222. }
  223. break;
  224. case 'built':
  225. if (options.log) {
  226. console.log(
  227. '[HMR] bundle ' +
  228. (obj.name ? "'" + obj.name + "' " : '') +
  229. 'rebuilt in ' +
  230. obj.time +
  231. 'ms'
  232. );
  233. }
  234. // fall through
  235. case 'sync':
  236. if (obj.name && options.name && obj.name !== options.name) {
  237. return;
  238. }
  239. var applyUpdate = true;
  240. if (obj.errors.length > 0) {
  241. if (reporter) reporter.problems('errors', obj);
  242. applyUpdate = false;
  243. } else if (obj.warnings.length > 0) {
  244. if (reporter) {
  245. var overlayShown = reporter.problems('warnings', obj);
  246. applyUpdate = overlayShown;
  247. }
  248. } else {
  249. if (reporter) {
  250. reporter.cleanProblemsCache();
  251. reporter.success();
  252. }
  253. }
  254. if (applyUpdate) {
  255. processUpdate(obj.hash, obj.modules, options);
  256. }
  257. break;
  258. default:
  259. if (customHandler) {
  260. customHandler(obj);
  261. }
  262. }
  263. if (subscribeAllHandler) {
  264. subscribeAllHandler(obj);
  265. }
  266. }
  267. if (module) {
  268. module.exports = {
  269. subscribeAll: function subscribeAll(handler) {
  270. subscribeAllHandler = handler;
  271. },
  272. subscribe: function subscribe(handler) {
  273. customHandler = handler;
  274. },
  275. useCustomOverlay: function useCustomOverlay(customOverlay) {
  276. if (reporter) reporter.useCustomOverlay(customOverlay);
  277. },
  278. setOptionsAndConnect: setOptionsAndConnect,
  279. };
  280. }