Skip to content

Commit

Permalink
refactor(client): separate into modules (#1948)
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroppy authored and evilebottnawi committed Jun 1, 2019
1 parent fb31770 commit ffb2c79
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 119 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -6,6 +6,7 @@ npm-debug.log
.nyc_output

client
!/test/client
coverage
ssl/*.pem
node_modules
Expand Down
169 changes: 51 additions & 118 deletions client-src/default/index.js
Expand Up @@ -8,28 +8,32 @@ const stripAnsi = require('strip-ansi');
const log = require('loglevel').getLogger('webpack-dev-server');
const socket = require('./socket');
const 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.
const scriptElements = document.scripts || [];
const 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.');
}
const sendMessage = require('./utils/sendMessage');
const reloadApp = require('./utils/reloadApp');
const getCurrentScriptSource = require('./utils/getCurrentScriptSource');

let urlParts;
let hotReload = true;
const status = {
isUnloading: false,
currentHash: '',
};
const options = {
hot: false,
hotReload: true,
liveReload: false,
initial: true,
useWarningOverlay: false,
useErrorOverlay: false,
useProgress: false,
};

self.addEventListener('beforeunload', () => {
status.isUnloading = true;
});

if (typeof window !== 'undefined') {
const qs = window.location.search.toLowerCase();
hotReload = qs.indexOf('hotreload=false') === -1;
options.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.
Expand All @@ -46,21 +50,12 @@ if (!urlParts.port || urlParts.port === '0') {
urlParts.port = self.location.port;
}

let hot = false;
let liveReload = false;
let initial = true;
let currentHash = '';
let useWarningOverlay = false;
let useErrorOverlay = false;
let useProgress = false;

const INFO = 'info';
const WARN = 'warn';
const ERROR = 'error';
const DEBUG = 'debug';
const TRACE = 'trace';
const SILENT = 'silent';

// deprecated
// TODO: remove these at major released
// https://github.com/webpack/webpack-dev-server/pull/1825
Expand All @@ -70,49 +65,32 @@ const 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${type}`,
data,
},
'*'
);
}
}

const onSocketMsg = {
hot() {
hot = true;
options.hot = true;
log.info('[WDS] Hot Module Replacement enabled.');
},
liveReload() {
liveReload = true;
options.liveReload = true;
log.info('[WDS] Live Reloading enabled.');
},
invalid() {
log.info('[WDS] App updated. Recompiling...');
// fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
if (useWarningOverlay || useErrorOverlay) {
if (options.useWarningOverlay || options.useErrorOverlay) {
overlay.clear();
}
sendMsg('Invalid');
sendMessage('Invalid');
},
hash(hash) {
currentHash = hash;
status.currentHash = hash;
},
'still-ok': function stillOk() {
log.info('[WDS] Nothing changed.');
if (useWarningOverlay || useErrorOverlay) {
if (options.useWarningOverlay || options.useErrorOverlay) {
overlay.clear();
}
sendMsg('StillOk');
sendMessage('StillOk');
},
'log-level': function logLevel(level) {
const hotCtx = require.context('webpack/hot', false, /^\.\/log$/);
Expand Down Expand Up @@ -144,34 +122,34 @@ const onSocketMsg = {
overlay(value) {
if (typeof document !== 'undefined') {
if (typeof value === 'boolean') {
useWarningOverlay = false;
useErrorOverlay = value;
options.useWarningOverlay = false;
options.useErrorOverlay = value;
} else if (value) {
useWarningOverlay = value.warnings;
useErrorOverlay = value.errors;
options.useWarningOverlay = value.warnings;
options.useErrorOverlay = value.errors;
}
}
},
progress(progress) {
if (typeof document !== 'undefined') {
useProgress = progress;
options.useProgress = progress;
}
},
'progress-update': function progressUpdate(data) {
if (useProgress) {
if (options.useProgress) {
log.info(`[WDS] ${data.percent}% - ${data.msg}.`);
}
sendMsg('Progress', data);
sendMessage('Progress', data);
},
ok() {
sendMsg('Ok');
if (useWarningOverlay || useErrorOverlay) {
sendMessage('Ok');
if (options.useWarningOverlay || options.useErrorOverlay) {
overlay.clear();
}
if (initial) {
return (initial = false);
if (options.initial) {
return (options.initial = false);
} // eslint-disable-line no-return-assign
reloadApp();
reloadApp(options, status);
},
'content-changed': function contentChanged() {
log.info('[WDS] Content base changed. Reloading...');
Expand All @@ -180,42 +158,41 @@ const onSocketMsg = {
warnings(warnings) {
log.warn('[WDS] Warnings while compiling.');
const strippedWarnings = warnings.map((warning) => stripAnsi(warning));
sendMsg('Warnings', strippedWarnings);
sendMessage('Warnings', strippedWarnings);
for (let i = 0; i < strippedWarnings.length; i++) {
log.warn(strippedWarnings[i]);
}
if (useWarningOverlay) {
if (options.useWarningOverlay) {
overlay.showMessage(warnings);
}

if (initial) {
return (initial = false);
if (options.initial) {
return (options.initial = false);
} // eslint-disable-line no-return-assign
reloadApp();
reloadApp(options, status);
},
errors(errors) {
log.error('[WDS] Errors while compiling. Reload prevented.');
const strippedErrors = errors.map((error) => stripAnsi(error));
sendMsg('Errors', strippedErrors);
sendMessage('Errors', strippedErrors);
for (let i = 0; i < strippedErrors.length; i++) {
log.error(strippedErrors[i]);
}
if (useErrorOverlay) {
if (options.useErrorOverlay) {
overlay.showMessage(errors);
}
initial = false;
options.initial = false;
},
error(error) {
log.error(error);
},
close() {
log.error('[WDS] Disconnected!');
sendMsg('Close');
sendMessage('Close');
},
};

let hostname = urlParts.hostname;
let protocol = urlParts.protocol;
let { hostname, protocol } = urlParts;

// check ipv4 and ipv6 `all hostname`
if (hostname === '0.0.0.0' || hostname === '::') {
Expand Down Expand Up @@ -270,47 +247,3 @@ const socketUrl = url.format({
});

socket(socketUrl, onSocketMsg);

let isUnloading = false;
self.addEventListener('beforeunload', () => {
isUnloading = true;
});

function reloadApp() {
if (isUnloading || !hotReload) {
return;
}
if (hot) {
log.info('[WDS] App hot update...');
// eslint-disable-next-line global-require
const hotEmitter = require('webpack/hot/emitter');
hotEmitter.emit('webpackHotUpdate', currentHash);
if (typeof self !== 'undefined' && self.window) {
// broadcast update to window
self.postMessage(`webpackHotUpdate${currentHash}`, '*');
}
}
// allow refreshing the page only if liveReload isn't disabled
else if (liveReload) {
let rootWindow = self;
// use parent window for reload (in case we're in an iframe with no valid src)
const intervalId = self.setInterval(() => {
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();
}
}
20 changes: 20 additions & 0 deletions client-src/default/utils/getCurrentScriptSource.js
@@ -0,0 +1,20 @@
'use strict';

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.
const scriptElements = document.scripts || [];
const 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.');
}

module.exports = getCurrentScriptSource;
49 changes: 49 additions & 0 deletions client-src/default/utils/reloadApp.js
@@ -0,0 +1,49 @@
'use strict';

/* global WorkerGlobalScope self */

const log = require('loglevel').getLogger('webpack-dev-server');

function reloadApp(
{ hotReload, hot, liveReload },
{ isUnloading, currentHash }
) {
if (isUnloading || !hotReload) {
return;
}
if (hot) {
log.info('[WDS] App hot update...');
// eslint-disable-next-line global-require
const hotEmitter = require('webpack/hot/emitter');
hotEmitter.emit('webpackHotUpdate', currentHash);
if (typeof self !== 'undefined' && self.window) {
// broadcast update to window
self.postMessage(`webpackHotUpdate${currentHash}`, '*');
}
}
// allow refreshing the page only if liveReload isn't disabled
else if (liveReload) {
let rootWindow = self;
// use parent window for reload (in case we're in an iframe with no valid src)
const intervalId = self.setInterval(() => {
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();
}
}

module.exports = reloadApp;
22 changes: 22 additions & 0 deletions client-src/default/utils/sendMessage.js
@@ -0,0 +1,22 @@
'use strict';

/* global __resourceQuery WorkerGlobalScope self */

// 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${type}`,
data,
},
'*'
);
}
}

module.exports = sendMsg;
2 changes: 1 addition & 1 deletion test/__snapshots__/Routes.test.js.snap

Large diffs are not rendered by default.

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getCurrentScriptSource should fail when script source doesn't exist 1`] = `[Error: [WDS] Failed to get current script source.]`;

0 comments on commit ffb2c79

Please sign in to comment.