overlay.js 3.64 KB
// 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 = "black";
	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 onIframeLoad() {
		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 onOverlayDivReady(overlayDiv) {
		// Make it look similar to our terminal.
		overlayDiv.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]);
}