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