UNPKG

8.29 kBJavaScriptView Raw
1'use strict';
2/* global __resourceQuery WorkerGlobalScope self */
3
4/* eslint prefer-destructuring: off */
5
6var querystring = require('querystring');
7
8var url = require('url');
9
10var stripAnsi = require('strip-ansi');
11
12var log = require('loglevel').getLogger('webpack-dev-server');
13
14var socket = require('./socket');
15
16var overlay = require('./overlay');
17
18function getCurrentScriptSource() {
19 // `document.currentScript` is the most accurate way to find the current script,
20 // but is not supported in all browsers.
21 if (document.currentScript) {
22 return document.currentScript.getAttribute('src');
23 } // Fall back to getting all scripts in the document.
24
25
26 var scriptElements = document.scripts || [];
27 var currentScript = scriptElements[scriptElements.length - 1];
28
29 if (currentScript) {
30 return currentScript.getAttribute('src');
31 } // Fail as there was no script to use.
32
33
34 throw new Error('[WDS] Failed to get current script source.');
35}
36
37var urlParts;
38var hotReload = true;
39
40if (typeof window !== 'undefined') {
41 var qs = window.location.search.toLowerCase();
42 hotReload = qs.indexOf('hotreload=false') === -1;
43}
44
45if (typeof __resourceQuery === 'string' && __resourceQuery) {
46 // If this bundle is inlined, use the resource query to get the correct url.
47 urlParts = url.parse(__resourceQuery.substr(1));
48} else {
49 // Else, get the url from the <script> this file was called with.
50 var scriptHost = getCurrentScriptSource(); // eslint-disable-next-line no-useless-escape
51
52 scriptHost = scriptHost.replace(/\/[^\/]+$/, '');
53 urlParts = url.parse(scriptHost || '/', false, true);
54}
55
56if (!urlParts.port || urlParts.port === '0') {
57 urlParts.port = self.location.port;
58}
59
60var _hot = false;
61var initial = true;
62var currentHash = '';
63var useWarningOverlay = false;
64var useErrorOverlay = false;
65var useProgress = false;
66var INFO = 'info';
67var WARNING = 'warning';
68var ERROR = 'error';
69var NONE = 'none'; // Set the default log level
70
71log.setDefaultLevel(INFO); // Send messages to the outside, so plugins can consume it.
72
73function sendMsg(type, data) {
74 if (typeof self !== 'undefined' && (typeof WorkerGlobalScope === 'undefined' || !(self instanceof WorkerGlobalScope))) {
75 self.postMessage({
76 type: "webpack".concat(type),
77 data: data
78 }, '*');
79 }
80}
81
82var onSocketMsg = {
83 hot: function hot() {
84 _hot = true;
85 log.info('[WDS] Hot Module Replacement enabled.');
86 },
87 invalid: function invalid() {
88 log.info('[WDS] App updated. Recompiling...'); // fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
89
90 if (useWarningOverlay || useErrorOverlay) overlay.clear();
91 sendMsg('Invalid');
92 },
93 hash: function hash(_hash) {
94 currentHash = _hash;
95 },
96 'still-ok': function stillOk() {
97 log.info('[WDS] Nothing changed.');
98 if (useWarningOverlay || useErrorOverlay) overlay.clear();
99 sendMsg('StillOk');
100 },
101 'log-level': function logLevel(level) {
102 var hotCtx = require.context('webpack/hot', false, /^\.\/log$/);
103
104 if (hotCtx.keys().indexOf('./log') !== -1) {
105 hotCtx('./log').setLogLevel(level);
106 }
107
108 switch (level) {
109 case INFO:
110 case ERROR:
111 log.setLevel(level);
112 break;
113
114 case WARNING:
115 // loglevel's warning name is different from webpack's
116 log.setLevel('warn');
117 break;
118
119 case NONE:
120 log.disableAll();
121 break;
122
123 default:
124 log.error("[WDS] Unknown clientLogLevel '".concat(level, "'"));
125 }
126 },
127 overlay: function overlay(value) {
128 if (typeof document !== 'undefined') {
129 if (typeof value === 'boolean') {
130 useWarningOverlay = false;
131 useErrorOverlay = value;
132 } else if (value) {
133 useWarningOverlay = value.warnings;
134 useErrorOverlay = value.errors;
135 }
136 }
137 },
138 progress: function progress(_progress) {
139 if (typeof document !== 'undefined') {
140 useProgress = _progress;
141 }
142 },
143 'progress-update': function progressUpdate(data) {
144 if (useProgress) log.info("[WDS] ".concat(data.percent, "% - ").concat(data.msg, "."));
145 sendMsg('Progress', data);
146 },
147 ok: function ok() {
148 sendMsg('Ok');
149 if (useWarningOverlay || useErrorOverlay) overlay.clear();
150 if (initial) return initial = false; // eslint-disable-line no-return-assign
151
152 reloadApp();
153 },
154 'content-changed': function contentChanged() {
155 log.info('[WDS] Content base changed. Reloading...');
156 self.location.reload();
157 },
158 warnings: function warnings(_warnings) {
159 log.warn('[WDS] Warnings while compiling.');
160
161 var strippedWarnings = _warnings.map(function (warning) {
162 return stripAnsi(warning);
163 });
164
165 sendMsg('Warnings', strippedWarnings);
166
167 for (var i = 0; i < strippedWarnings.length; i++) {
168 log.warn(strippedWarnings[i]);
169 }
170
171 if (useWarningOverlay) overlay.showMessage(_warnings);
172 if (initial) return initial = false; // eslint-disable-line no-return-assign
173
174 reloadApp();
175 },
176 errors: function errors(_errors) {
177 log.error('[WDS] Errors while compiling. Reload prevented.');
178
179 var strippedErrors = _errors.map(function (error) {
180 return stripAnsi(error);
181 });
182
183 sendMsg('Errors', strippedErrors);
184
185 for (var i = 0; i < strippedErrors.length; i++) {
186 log.error(strippedErrors[i]);
187 }
188
189 if (useErrorOverlay) overlay.showMessage(_errors);
190 initial = false;
191 },
192 error: function error(_error) {
193 log.error(_error);
194 },
195 close: function close() {
196 log.error('[WDS] Disconnected!');
197 sendMsg('Close');
198 }
199};
200var hostname = urlParts.hostname;
201var protocol = urlParts.protocol; // check ipv4 and ipv6 `all hostname`
202
203if (hostname === '0.0.0.0' || hostname === '::') {
204 // why do we need this check?
205 // hostname n/a for file protocol (example, when using electron, ionic)
206 // see: https://github.com/webpack/webpack-dev-server/pull/384
207 // eslint-disable-next-line no-bitwise
208 if (self.location.hostname && !!~self.location.protocol.indexOf('http')) {
209 hostname = self.location.hostname;
210 }
211} // `hostname` can be empty when the script path is relative. In that case, specifying
212// a protocol would result in an invalid URL.
213// When https is used in the app, secure websockets are always necessary
214// because the browser doesn't accept non-secure websockets.
215
216
217if (hostname && (self.location.protocol === 'https:' || urlParts.hostname === '0.0.0.0')) {
218 protocol = self.location.protocol;
219}
220
221var socketUrl = url.format({
222 protocol: protocol,
223 auth: urlParts.auth,
224 hostname: hostname,
225 port: urlParts.port,
226 // If sockPath is provided it'll be passed in via the __resourceQuery as a
227 // query param so it has to be parsed out of the querystring in order for the
228 // client to open the socket to the correct location.
229 pathname: urlParts.path == null || urlParts.path === '/' ? '/sockjs-node' : querystring.parse(urlParts.path).sockPath || urlParts.path
230});
231socket(socketUrl, onSocketMsg);
232var isUnloading = false;
233self.addEventListener('beforeunload', function () {
234 isUnloading = true;
235});
236
237function reloadApp() {
238 if (isUnloading || !hotReload) {
239 return;
240 }
241
242 if (_hot) {
243 log.info('[WDS] App hot update...'); // eslint-disable-next-line global-require
244
245 var hotEmitter = require('webpack/hot/emitter');
246
247 hotEmitter.emit('webpackHotUpdate', currentHash);
248
249 if (typeof self !== 'undefined' && self.window) {
250 // broadcast update to window
251 self.postMessage("webpackHotUpdate".concat(currentHash), '*');
252 }
253 } else {
254 var rootWindow = self; // use parent window for reload (in case we're in an iframe with no valid src)
255
256 var intervalId = self.setInterval(function () {
257 if (rootWindow.location.protocol !== 'about:') {
258 // reload immediately if protocol is valid
259 applyReload(rootWindow, intervalId);
260 } else {
261 rootWindow = rootWindow.parent;
262
263 if (rootWindow.parent === rootWindow) {
264 // if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways
265 applyReload(rootWindow, intervalId);
266 }
267 }
268 });
269 }
270
271 function applyReload(rootWindow, intervalId) {
272 clearInterval(intervalId);
273 log.info('[WDS] App updated. Reloading...');
274 rootWindow.location.reload();
275 }
276}
\No newline at end of file