Skip to content

Commit

Permalink
Add built client
Browse files Browse the repository at this point in the history
Add transpiled client from client-src so that git+https or github: remote works
while installing package.
  • Loading branch information
Rohan Bhanderi committed Nov 1, 2018
1 parent 7cdfb74 commit 1d2adbd
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
@@ -1,6 +1,5 @@
npm-debug.log
node_modules
/client
/coverage
/ssl/*.pem
.idea/
Expand Down
1 change: 1 addition & 0 deletions client/index.bundle.js

Large diffs are not rendered by default.

253 changes: 253 additions & 0 deletions client/index.js
@@ -0,0 +1,253 @@
'use strict';

/* global __resourceQuery WorkerGlobalScope self */
/* eslint prefer-destructuring: off */

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 = void 0;
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' + 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 \'' + 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] ' + data.percent + '% - ' + data.msg + '.');
},
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,
pathname: urlParts.path == null || urlParts.path === '/' ? '/sockjs-node' : 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' + 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();
}
}
24 changes: 24 additions & 0 deletions client/live.bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions client/live.html
@@ -0,0 +1 @@
<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/><script type="text/javascript" charset="utf-8" src="/__webpack_dev_server__/live.bundle.js"></script></head><body></body></html>
126 changes: 126 additions & 0 deletions client/overlay.js
@@ -0,0 +1,126 @@
'use strict';

// The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app)
// They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware).

var ansiHTML = require('ansi-html');
var Entities = require('html-entities').AllHtmlEntities;

var entities = new Entities();

var colors = {
reset: ['transparent', 'transparent'],
black: '181818',
red: 'E36049',
green: 'B3CB74',
yellow: 'FFD080',
blue: '7CAFC2',
magenta: '7FACCA',
cyan: 'C3C2EF',
lightgrey: 'EBE7E3',
darkgrey: '6D7891'
};
ansiHTML.setColors(colors);

function createOverlayIframe(onIframeLoad) {
var iframe = document.createElement('iframe');
iframe.id = 'webpack-dev-server-client-overlay';
iframe.src = 'about:blank';
iframe.style.position = 'fixed';
iframe.style.left = 0;
iframe.style.top = 0;
iframe.style.right = 0;
iframe.style.bottom = 0;
iframe.style.width = '100vw';
iframe.style.height = '100vh';
iframe.style.border = 'none';
iframe.style.zIndex = 9999999999;
iframe.onload = onIframeLoad;
return iframe;
}

function addOverlayDivTo(iframe) {
var div = iframe.contentDocument.createElement('div');
div.id = 'webpack-dev-server-client-overlay-div';
div.style.position = 'fixed';
div.style.boxSizing = 'border-box';
div.style.left = 0;
div.style.top = 0;
div.style.right = 0;
div.style.bottom = 0;
div.style.width = '100vw';
div.style.height = '100vh';
div.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
div.style.color = '#E8E8E8';
div.style.fontFamily = 'Menlo, Consolas, monospace';
div.style.fontSize = 'large';
div.style.padding = '2rem';
div.style.lineHeight = '1.2';
div.style.whiteSpace = 'pre-wrap';
div.style.overflow = 'auto';
iframe.contentDocument.body.appendChild(div);
return div;
}

var overlayIframe = null;
var overlayDiv = null;
var lastOnOverlayDivReady = null;

function ensureOverlayDivExists(onOverlayDivReady) {
if (overlayDiv) {
// Everything is ready, call the callback right away.
onOverlayDivReady(overlayDiv);
return;
}

// Creating an iframe may be asynchronous so we'll schedule the callback.
// In case of multiple calls, last callback wins.
lastOnOverlayDivReady = onOverlayDivReady;

if (overlayIframe) {
// We're already creating it.
return;
}

// Create iframe and, when it is ready, a div inside it.
overlayIframe = createOverlayIframe(function () {
overlayDiv = addOverlayDivTo(overlayIframe);
// Now we can talk!
lastOnOverlayDivReady(overlayDiv);
});

// Zalgo alert: onIframeLoad() will be called either synchronously
// or asynchronously depending on the browser.
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
document.body.appendChild(overlayIframe);
}

function showMessageOverlay(message) {
ensureOverlayDivExists(function (div) {
// Make it look similar to our terminal.
div.innerHTML = '<span style="color: #' + colors.red + '">Failed to compile.</span><br><br>' + ansiHTML(entities.encode(message));
});
}

function destroyErrorOverlay() {
if (!overlayDiv) {
// It is not there in the first place.
return;
}

// Clean up and reset internal state.
document.body.removeChild(overlayIframe);
overlayDiv = null;
overlayIframe = null;
lastOnOverlayDivReady = null;
}

// Successful compilation.
exports.clear = function handleSuccess() {
destroyErrorOverlay();
};

// Compilation with errors (e.g. syntax error or missing modules).
exports.showMessage = function handleMessage(messages) {
showMessageOverlay(messages[0]);
};

0 comments on commit 1d2adbd

Please sign in to comment.