You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
8.1 KiB
276 lines
8.1 KiB
'use strict'; |
|
/* global __resourceQuery WorkerGlobalScope self */ |
|
|
|
/* eslint prefer-destructuring: off */ |
|
|
|
var querystring = require('querystring'); |
|
|
|
var url = require('url'); |
|
|
|
var stripAnsi = require('strip-ansi'); |
|
|
|
var log = require('loglevel').getLogger('webpack-dev-server'); |
|
|
|
var socket = require('./socket'); |
|
|
|
var overlay = require('./overlay'); |
|
|
|
function getCurrentScriptSource() { |
|
// `document.currentScript` is the most accurate way to find the current script, |
|
// but is not supported in all browsers. |
|
if (document.currentScript) { |
|
return document.currentScript.getAttribute('src'); |
|
} // Fall back to getting all scripts in the document. |
|
|
|
|
|
var scriptElements = document.scripts || []; |
|
var currentScript = scriptElements[scriptElements.length - 1]; |
|
|
|
if (currentScript) { |
|
return currentScript.getAttribute('src'); |
|
} // Fail as there was no script to use. |
|
|
|
|
|
throw new Error('[WDS] Failed to get current script source.'); |
|
} |
|
|
|
var urlParts; |
|
var hotReload = true; |
|
|
|
if (typeof window !== 'undefined') { |
|
var qs = window.location.search.toLowerCase(); |
|
hotReload = qs.indexOf('hotreload=false') === -1; |
|
} |
|
|
|
if (typeof __resourceQuery === 'string' && __resourceQuery) { |
|
// If this bundle is inlined, use the resource query to get the correct url. |
|
urlParts = url.parse(__resourceQuery.substr(1)); |
|
} else { |
|
// Else, get the url from the <script> this file was called with. |
|
var scriptHost = getCurrentScriptSource(); // eslint-disable-next-line no-useless-escape |
|
|
|
scriptHost = scriptHost.replace(/\/[^\/]+$/, ''); |
|
urlParts = url.parse(scriptHost || '/', false, true); |
|
} |
|
|
|
if (!urlParts.port || urlParts.port === '0') { |
|
urlParts.port = self.location.port; |
|
} |
|
|
|
var _hot = false; |
|
var initial = true; |
|
var currentHash = ''; |
|
var useWarningOverlay = false; |
|
var useErrorOverlay = false; |
|
var useProgress = false; |
|
var INFO = 'info'; |
|
var WARNING = 'warning'; |
|
var ERROR = 'error'; |
|
var NONE = 'none'; // Set the default log level |
|
|
|
log.setDefaultLevel(INFO); // Send messages to the outside, so plugins can consume it. |
|
|
|
function sendMsg(type, data) { |
|
if (typeof self !== 'undefined' && (typeof WorkerGlobalScope === 'undefined' || !(self instanceof WorkerGlobalScope))) { |
|
self.postMessage({ |
|
type: "webpack".concat(type), |
|
data: data |
|
}, '*'); |
|
} |
|
} |
|
|
|
var onSocketMsg = { |
|
hot: function hot() { |
|
_hot = true; |
|
log.info('[WDS] Hot Module Replacement enabled.'); |
|
}, |
|
invalid: function invalid() { |
|
log.info('[WDS] App updated. Recompiling...'); // fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. |
|
|
|
if (useWarningOverlay || useErrorOverlay) overlay.clear(); |
|
sendMsg('Invalid'); |
|
}, |
|
hash: function hash(_hash) { |
|
currentHash = _hash; |
|
}, |
|
'still-ok': function stillOk() { |
|
log.info('[WDS] Nothing changed.'); |
|
if (useWarningOverlay || useErrorOverlay) overlay.clear(); |
|
sendMsg('StillOk'); |
|
}, |
|
'log-level': function logLevel(level) { |
|
var hotCtx = require.context('webpack/hot', false, /^\.\/log$/); |
|
|
|
if (hotCtx.keys().indexOf('./log') !== -1) { |
|
hotCtx('./log').setLogLevel(level); |
|
} |
|
|
|
switch (level) { |
|
case INFO: |
|
case ERROR: |
|
log.setLevel(level); |
|
break; |
|
|
|
case WARNING: |
|
// loglevel's warning name is different from webpack's |
|
log.setLevel('warn'); |
|
break; |
|
|
|
case NONE: |
|
log.disableAll(); |
|
break; |
|
|
|
default: |
|
log.error("[WDS] Unknown clientLogLevel '".concat(level, "'")); |
|
} |
|
}, |
|
overlay: function overlay(value) { |
|
if (typeof document !== 'undefined') { |
|
if (typeof value === 'boolean') { |
|
useWarningOverlay = false; |
|
useErrorOverlay = value; |
|
} else if (value) { |
|
useWarningOverlay = value.warnings; |
|
useErrorOverlay = value.errors; |
|
} |
|
} |
|
}, |
|
progress: function progress(_progress) { |
|
if (typeof document !== 'undefined') { |
|
useProgress = _progress; |
|
} |
|
}, |
|
'progress-update': function progressUpdate(data) { |
|
if (useProgress) log.info("[WDS] ".concat(data.percent, "% - ").concat(data.msg, ".")); |
|
sendMsg('Progress', data); |
|
}, |
|
ok: function ok() { |
|
sendMsg('Ok'); |
|
if (useWarningOverlay || useErrorOverlay) overlay.clear(); |
|
if (initial) return initial = false; // eslint-disable-line no-return-assign |
|
|
|
reloadApp(); |
|
}, |
|
'content-changed': function contentChanged() { |
|
log.info('[WDS] Content base changed. Reloading...'); |
|
self.location.reload(); |
|
}, |
|
warnings: function warnings(_warnings) { |
|
log.warn('[WDS] Warnings while compiling.'); |
|
|
|
var strippedWarnings = _warnings.map(function (warning) { |
|
return stripAnsi(warning); |
|
}); |
|
|
|
sendMsg('Warnings', strippedWarnings); |
|
|
|
for (var i = 0; i < strippedWarnings.length; i++) { |
|
log.warn(strippedWarnings[i]); |
|
} |
|
|
|
if (useWarningOverlay) overlay.showMessage(_warnings); |
|
if (initial) return initial = false; // eslint-disable-line no-return-assign |
|
|
|
reloadApp(); |
|
}, |
|
errors: function errors(_errors) { |
|
log.error('[WDS] Errors while compiling. Reload prevented.'); |
|
|
|
var strippedErrors = _errors.map(function (error) { |
|
return stripAnsi(error); |
|
}); |
|
|
|
sendMsg('Errors', strippedErrors); |
|
|
|
for (var i = 0; i < strippedErrors.length; i++) { |
|
log.error(strippedErrors[i]); |
|
} |
|
|
|
if (useErrorOverlay) overlay.showMessage(_errors); |
|
initial = false; |
|
}, |
|
error: function error(_error) { |
|
log.error(_error); |
|
}, |
|
close: function close() { |
|
log.error('[WDS] Disconnected!'); |
|
sendMsg('Close'); |
|
} |
|
}; |
|
var hostname = urlParts.hostname; |
|
var protocol = urlParts.protocol; // check ipv4 and ipv6 `all hostname` |
|
|
|
if (hostname === '0.0.0.0' || hostname === '::') { |
|
// why do we need this check? |
|
// hostname n/a for file protocol (example, when using electron, ionic) |
|
// see: https://github.com/webpack/webpack-dev-server/pull/384 |
|
// eslint-disable-next-line no-bitwise |
|
if (self.location.hostname && !!~self.location.protocol.indexOf('http')) { |
|
hostname = self.location.hostname; |
|
} |
|
} // `hostname` can be empty when the script path is relative. In that case, specifying |
|
// a protocol would result in an invalid URL. |
|
// When https is used in the app, secure websockets are always necessary |
|
// because the browser doesn't accept non-secure websockets. |
|
|
|
|
|
if (hostname && (self.location.protocol === 'https:' || urlParts.hostname === '0.0.0.0')) { |
|
protocol = self.location.protocol; |
|
} |
|
|
|
var socketUrl = url.format({ |
|
protocol: protocol, |
|
auth: urlParts.auth, |
|
hostname: hostname, |
|
port: urlParts.port, |
|
// If sockPath is provided it'll be passed in via the __resourceQuery as a |
|
// query param so it has to be parsed out of the querystring in order for the |
|
// client to open the socket to the correct location. |
|
pathname: urlParts.path == null || urlParts.path === '/' ? '/sockjs-node' : querystring.parse(urlParts.path).sockPath || urlParts.path |
|
}); |
|
socket(socketUrl, onSocketMsg); |
|
var isUnloading = false; |
|
self.addEventListener('beforeunload', function () { |
|
isUnloading = true; |
|
}); |
|
|
|
function reloadApp() { |
|
if (isUnloading || !hotReload) { |
|
return; |
|
} |
|
|
|
if (_hot) { |
|
log.info('[WDS] App hot update...'); // eslint-disable-next-line global-require |
|
|
|
var hotEmitter = require('webpack/hot/emitter'); |
|
|
|
hotEmitter.emit('webpackHotUpdate', currentHash); |
|
|
|
if (typeof self !== 'undefined' && self.window) { |
|
// broadcast update to window |
|
self.postMessage("webpackHotUpdate".concat(currentHash), '*'); |
|
} |
|
} else { |
|
var rootWindow = self; // use parent window for reload (in case we're in an iframe with no valid src) |
|
|
|
var intervalId = self.setInterval(function () { |
|
if (rootWindow.location.protocol !== 'about:') { |
|
// reload immediately if protocol is valid |
|
applyReload(rootWindow, intervalId); |
|
} else { |
|
rootWindow = rootWindow.parent; |
|
|
|
if (rootWindow.parent === rootWindow) { |
|
// if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways |
|
applyReload(rootWindow, intervalId); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function applyReload(rootWindow, intervalId) { |
|
clearInterval(intervalId); |
|
log.info('[WDS] App updated. Reloading...'); |
|
rootWindow.location.reload(); |
|
} |
|
} |