mirror of https://github.com/ghostfolio/ghostfolio
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.
503 lines
19 KiB
503 lines
19 KiB
/**
|
|
* The following code is modified based on
|
|
* https://github.com/webpack/webpack-dev-server
|
|
*
|
|
* MIT Licensed
|
|
* Author Tobias Koppers @sokra
|
|
* Copyright (c) JS Foundation and other contributors
|
|
* https://github.com/webpack/webpack-dev-server/blob/main/LICENSE
|
|
*/
|
|
var __assign = (this && this.__assign) || function () {
|
|
__assign = Object.assign || function(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
// 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).
|
|
import ansiHTML from './utils/ansiHTML';
|
|
var getCodePoint = !!String.prototype.codePointAt
|
|
? function (input, position) {
|
|
return input.codePointAt(position);
|
|
}
|
|
: function (input, position) {
|
|
return (input.charCodeAt(position) - 0xd800) * 0x400 +
|
|
input.charCodeAt(position + 1) -
|
|
0xdc00 +
|
|
0x10000;
|
|
};
|
|
var replaceUsingRegExp = function (macroText, macroRegExp, macroReplacer) {
|
|
macroRegExp.lastIndex = 0;
|
|
var replaceMatch = macroRegExp.exec(macroText);
|
|
var replaceResult;
|
|
if (replaceMatch) {
|
|
replaceResult = '';
|
|
var replaceLastIndex = 0;
|
|
do {
|
|
if (replaceLastIndex !== replaceMatch.index) {
|
|
replaceResult += macroText.slice(replaceLastIndex, replaceMatch.index);
|
|
}
|
|
var replaceInput = replaceMatch[0];
|
|
replaceResult += macroReplacer(replaceInput);
|
|
replaceLastIndex = replaceMatch.index + replaceInput.length;
|
|
} while ((replaceMatch = macroRegExp.exec(macroText)));
|
|
if (replaceLastIndex !== macroText.length) {
|
|
replaceResult += macroText.slice(replaceLastIndex);
|
|
}
|
|
}
|
|
else {
|
|
replaceResult = macroText;
|
|
}
|
|
return replaceResult;
|
|
};
|
|
var references = {
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": ''',
|
|
'&': '&',
|
|
};
|
|
function encode(text) {
|
|
if (!text) {
|
|
return '';
|
|
}
|
|
return replaceUsingRegExp(text, /[<>'"&]/g, function (input) {
|
|
var result = references[input];
|
|
if (!result) {
|
|
var code = input.length > 1 ? getCodePoint(input, 0) : input.charCodeAt(0);
|
|
result = "&#".concat(code, ";");
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
/**
|
|
* A simplified `createMachine` from `@xstate/fsm` with the following differences:
|
|
* - the returned machine is technically a "service". No `interpret(machine).start()` is needed.
|
|
* - the state definition only support `on` and target must be declared with { target: 'nextState', actions: [] } explicitly.
|
|
* - event passed to `send` must be an object with `type` property.
|
|
* - actions implementation will be [assign action](https://xstate.js.org/docs/guides/context.html#assign-action) if you return any value.
|
|
* Do not return anything if you just want to invoke side effect.
|
|
*
|
|
* The goal of this custom function is to avoid installing the entire `'xstate/fsm'` package, while enabling modeling using
|
|
* state machine. You can copy the first parameter into the editor at https://stately.ai/viz to visualize the state machine.
|
|
*/
|
|
function createMachine(_a, _b) {
|
|
var states = _a.states, context = _a.context, initial = _a.initial;
|
|
var actions = _b.actions;
|
|
var currentState = initial;
|
|
var currentContext = context;
|
|
return {
|
|
send: function (event) {
|
|
var currentStateOn = states[currentState].on;
|
|
var transitionConfig = currentStateOn && currentStateOn[event.type];
|
|
if (transitionConfig) {
|
|
currentState = transitionConfig.target;
|
|
if (transitionConfig.actions) {
|
|
transitionConfig.actions.forEach(function (actName) {
|
|
var actionImpl = actions[actName];
|
|
var nextContextValue = actionImpl && actionImpl(currentContext, event);
|
|
if (nextContextValue) {
|
|
currentContext = __assign(__assign({}, currentContext), nextContextValue);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
};
|
|
}
|
|
var createOverlayMachine = function (options) {
|
|
var hideOverlay = options.hideOverlay, showOverlay = options.showOverlay;
|
|
return createMachine({
|
|
initial: 'hidden',
|
|
context: {
|
|
level: 'error',
|
|
messages: [],
|
|
messageSource: 'build',
|
|
},
|
|
states: {
|
|
hidden: {
|
|
on: {
|
|
BUILD_ERROR: {
|
|
target: 'displayBuildError',
|
|
actions: ['setMessages', 'showOverlay'],
|
|
},
|
|
RUNTIME_ERROR: {
|
|
target: 'displayRuntimeError',
|
|
actions: ['setMessages', 'showOverlay'],
|
|
},
|
|
},
|
|
},
|
|
displayBuildError: {
|
|
on: {
|
|
DISMISS: {
|
|
target: 'hidden',
|
|
actions: ['dismissMessages', 'hideOverlay'],
|
|
},
|
|
BUILD_ERROR: {
|
|
target: 'displayBuildError',
|
|
actions: ['appendMessages', 'showOverlay'],
|
|
},
|
|
},
|
|
},
|
|
displayRuntimeError: {
|
|
on: {
|
|
DISMISS: {
|
|
target: 'hidden',
|
|
actions: ['dismissMessages', 'hideOverlay'],
|
|
},
|
|
RUNTIME_ERROR: {
|
|
target: 'displayRuntimeError',
|
|
actions: ['appendMessages', 'showOverlay'],
|
|
},
|
|
BUILD_ERROR: {
|
|
target: 'displayBuildError',
|
|
actions: ['setMessages', 'showOverlay'],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, {
|
|
actions: {
|
|
dismissMessages: function () {
|
|
return {
|
|
messages: [],
|
|
level: 'error',
|
|
messageSource: 'build',
|
|
};
|
|
},
|
|
appendMessages: function (context, event) {
|
|
return {
|
|
messages: context.messages.concat(event.messages),
|
|
level: event.level || context.level,
|
|
messageSource: event.type === 'RUNTIME_ERROR' ? 'runtime' : 'build',
|
|
};
|
|
},
|
|
setMessages: function (context, event) {
|
|
return {
|
|
messages: event.messages,
|
|
level: event.level || context.level,
|
|
messageSource: event.type === 'RUNTIME_ERROR' ? 'runtime' : 'build',
|
|
};
|
|
},
|
|
hideOverlay: hideOverlay,
|
|
showOverlay: showOverlay,
|
|
},
|
|
});
|
|
};
|
|
var parseErrorToStacks = function (error) {
|
|
if (!error || !(error instanceof Error)) {
|
|
throw new Error('parseErrorToStacks expects Error object');
|
|
}
|
|
if (typeof error.stack === 'string') {
|
|
return error.stack
|
|
.split('\n')
|
|
.filter(function (stack) { return stack !== "Error: ".concat(error.message); });
|
|
}
|
|
};
|
|
var listenToRuntimeError = function (callback) {
|
|
window.addEventListener('error', callback);
|
|
return function cleanup() {
|
|
window.removeEventListener('error', callback);
|
|
};
|
|
};
|
|
var listenToUnhandledRejection = function (callback) {
|
|
window.addEventListener('unhandledrejection', callback);
|
|
return function cleanup() {
|
|
window.removeEventListener('unhandledrejection', callback);
|
|
};
|
|
};
|
|
// Styles are inspired by `react-error-overlay`
|
|
var msgStyles = {
|
|
error: {
|
|
backgroundColor: 'rgba(206, 17, 38, 0.1)',
|
|
color: '#fccfcf',
|
|
},
|
|
warning: {
|
|
backgroundColor: 'rgba(251, 245, 180, 0.1)',
|
|
color: '#fbf5b4',
|
|
},
|
|
};
|
|
var iframeStyle = {
|
|
position: 'fixed',
|
|
top: '0px',
|
|
left: '0px',
|
|
right: '0px',
|
|
bottom: '0px',
|
|
width: '100vw',
|
|
height: '100vh',
|
|
border: 'none',
|
|
'z-index': 9999999999,
|
|
};
|
|
var containerStyle = {
|
|
position: 'fixed',
|
|
boxSizing: 'border-box',
|
|
left: '0px',
|
|
top: '0px',
|
|
right: '0px',
|
|
bottom: '0px',
|
|
width: '100vw',
|
|
height: '100vh',
|
|
fontSize: 'large',
|
|
padding: '2rem 2rem 4rem 2rem',
|
|
lineHeight: '1.2',
|
|
whiteSpace: 'pre-wrap',
|
|
overflow: 'auto',
|
|
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
color: 'white',
|
|
};
|
|
var headerStyle = {
|
|
color: '#e83b46',
|
|
fontSize: '2em',
|
|
whiteSpace: 'pre-wrap',
|
|
fontFamily: 'sans-serif',
|
|
margin: '0 2rem 2rem 0',
|
|
flex: '0 0 auto',
|
|
maxHeight: '50%',
|
|
overflow: 'auto',
|
|
};
|
|
var dismissButtonStyle = {
|
|
color: '#ffffff',
|
|
lineHeight: '1rem',
|
|
fontSize: '1.5rem',
|
|
padding: '1rem',
|
|
cursor: 'pointer',
|
|
position: 'absolute',
|
|
right: '0px',
|
|
top: '0px',
|
|
backgroundColor: 'transparent',
|
|
border: 'none',
|
|
};
|
|
var msgTypeStyle = {
|
|
color: '#e83b46',
|
|
fontSize: '1.2em',
|
|
marginBottom: '1rem',
|
|
fontFamily: 'sans-serif',
|
|
};
|
|
var msgTextStyle = {
|
|
lineHeight: '1.5',
|
|
fontSize: '1rem',
|
|
fontFamily: 'Menlo, Consolas, monospace',
|
|
};
|
|
// ANSI HTML
|
|
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);
|
|
var formatProblem = function (type, item) {
|
|
var header = type === 'warning' ? 'WARNING' : 'ERROR';
|
|
var body = '';
|
|
if (typeof item === 'string') {
|
|
body += item;
|
|
}
|
|
else {
|
|
var file = item.file || '';
|
|
var moduleName = item.moduleName
|
|
? item.moduleName.indexOf('!') !== -1
|
|
? "".concat(item.moduleName.replace(/^(\s|\S)*!/, ''), " (").concat(item.moduleName, ")")
|
|
: "".concat(item.moduleName)
|
|
: '';
|
|
var loc = item.loc;
|
|
header += "".concat(moduleName || file
|
|
? " in ".concat(moduleName ? "".concat(moduleName).concat(file ? " (".concat(file, ")") : '') : file).concat(loc ? " ".concat(loc) : '')
|
|
: '');
|
|
body += item.message || '';
|
|
}
|
|
if (typeof item !== 'string' && Array.isArray(item.stack)) {
|
|
item.stack.forEach(function (stack) {
|
|
if (typeof stack === 'string') {
|
|
body += "\r\n".concat(stack);
|
|
}
|
|
});
|
|
}
|
|
return { header: header, body: body };
|
|
};
|
|
var createOverlay = function (options) {
|
|
var iframeContainerElement;
|
|
var containerElement;
|
|
var headerElement;
|
|
var onLoadQueue = [];
|
|
var overlayTrustedTypesPolicy;
|
|
function applyStyle(element, style) {
|
|
Object.keys(style).forEach(function (prop) {
|
|
element.style[prop] =
|
|
style[prop];
|
|
});
|
|
}
|
|
function createContainer(trustedTypesPolicyName) {
|
|
// Enable Trusted Types if they are available in the current browser.
|
|
if (window.trustedTypes) {
|
|
overlayTrustedTypesPolicy = window.trustedTypes.createPolicy(trustedTypesPolicyName || 'webpack-dev-server#overlay', {
|
|
createHTML: function (value) { return value; },
|
|
});
|
|
}
|
|
iframeContainerElement = document.createElement('iframe');
|
|
iframeContainerElement.id = 'webpack-dev-server-client-overlay';
|
|
iframeContainerElement.src = 'about:blank';
|
|
applyStyle(iframeContainerElement, iframeStyle);
|
|
iframeContainerElement.onload = function () {
|
|
var contentElement = (iframeContainerElement === null || iframeContainerElement === void 0 ? void 0 : iframeContainerElement.contentDocument).createElement('div');
|
|
containerElement = (iframeContainerElement === null || iframeContainerElement === void 0 ? void 0 : iframeContainerElement.contentDocument).createElement('div');
|
|
contentElement.id = 'webpack-dev-server-client-overlay-div';
|
|
applyStyle(contentElement, containerStyle);
|
|
headerElement = document.createElement('div');
|
|
headerElement.innerText = 'Compiled with problems:';
|
|
applyStyle(headerElement, headerStyle);
|
|
var closeButtonElement = document.createElement('button');
|
|
applyStyle(closeButtonElement, dismissButtonStyle);
|
|
closeButtonElement.innerText = '×';
|
|
closeButtonElement.ariaLabel = 'Dismiss';
|
|
closeButtonElement.addEventListener('click', function () {
|
|
// eslint-disable-next-line no-use-before-define
|
|
overlayService.send({ type: 'DISMISS' });
|
|
});
|
|
contentElement.appendChild(headerElement);
|
|
contentElement.appendChild(closeButtonElement);
|
|
contentElement.appendChild(containerElement);
|
|
(iframeContainerElement === null || iframeContainerElement === void 0 ? void 0 : iframeContainerElement.contentDocument).body.appendChild(contentElement);
|
|
onLoadQueue.forEach(function (onLoad) {
|
|
onLoad(contentElement);
|
|
});
|
|
onLoadQueue = [];
|
|
iframeContainerElement.onload = null;
|
|
};
|
|
document.body.appendChild(iframeContainerElement);
|
|
}
|
|
function ensureOverlayExists(callback, trustedTypesPolicyName) {
|
|
if (containerElement) {
|
|
containerElement.innerHTML = overlayTrustedTypesPolicy
|
|
? overlayTrustedTypesPolicy.createHTML('')
|
|
: '';
|
|
// Everything is ready, call the callback right away.
|
|
callback(containerElement);
|
|
return;
|
|
}
|
|
onLoadQueue.push(callback);
|
|
if (iframeContainerElement) {
|
|
return;
|
|
}
|
|
createContainer(trustedTypesPolicyName);
|
|
}
|
|
// Successful compilation.
|
|
function hide() {
|
|
if (!iframeContainerElement) {
|
|
return;
|
|
}
|
|
// Clean up and reset internal state.
|
|
document.body.removeChild(iframeContainerElement);
|
|
iframeContainerElement = null;
|
|
containerElement = null;
|
|
}
|
|
// Compilation with errors (e.g. syntax error or missing modules).
|
|
function show(type, messages, trustedTypesPolicyName, messageSource) {
|
|
ensureOverlayExists(function () {
|
|
headerElement.innerText =
|
|
messageSource === 'runtime'
|
|
? 'Uncaught runtime errors:'
|
|
: 'Compiled with problems:';
|
|
messages.forEach(function (message) {
|
|
var entryElement = document.createElement('div');
|
|
var msgStyle = type === 'warning' ? msgStyles.warning : msgStyles.error;
|
|
applyStyle(entryElement, __assign(__assign({}, msgStyle), { padding: '1rem 1rem 1.5rem 1rem' }));
|
|
var typeElement = document.createElement('div');
|
|
var _a = formatProblem(type, message), header = _a.header, body = _a.body;
|
|
typeElement.innerText = header;
|
|
applyStyle(typeElement, msgTypeStyle);
|
|
if (typeof message !== 'string' && message.moduleIdentifier) {
|
|
applyStyle(typeElement, { cursor: 'pointer' });
|
|
// element.dataset not supported in IE
|
|
typeElement.setAttribute('data-can-open', 'true');
|
|
typeElement.addEventListener('click', function () {
|
|
fetch("/webpack-dev-server/open-editor?fileName=".concat(message.moduleIdentifier));
|
|
});
|
|
}
|
|
// Make it look similar to our terminal.
|
|
var text = ansiHTML(encode(body));
|
|
var messageTextNode = document.createElement('div');
|
|
applyStyle(messageTextNode, msgTextStyle);
|
|
messageTextNode.innerHTML = overlayTrustedTypesPolicy
|
|
? overlayTrustedTypesPolicy.createHTML(text)
|
|
: text;
|
|
entryElement.appendChild(typeElement);
|
|
entryElement.appendChild(messageTextNode);
|
|
containerElement === null || containerElement === void 0 ? void 0 : containerElement.appendChild(entryElement);
|
|
});
|
|
}, trustedTypesPolicyName);
|
|
}
|
|
var handleEscapeKey;
|
|
var hideOverlayWithEscCleanup = function () {
|
|
window.removeEventListener('keydown', handleEscapeKey);
|
|
hide();
|
|
};
|
|
var overlayService = createOverlayMachine({
|
|
showOverlay: function (_a) {
|
|
var _b = _a.level, level = _b === void 0 ? 'error' : _b, messages = _a.messages, messageSource = _a.messageSource;
|
|
return show(level, messages, options.trustedTypesPolicyName, messageSource);
|
|
},
|
|
hideOverlay: hideOverlayWithEscCleanup,
|
|
});
|
|
/**
|
|
* ESC key press to dismiss the overlay.
|
|
*/
|
|
handleEscapeKey = function (event) {
|
|
if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) {
|
|
overlayService.send({ type: 'DISMISS' });
|
|
}
|
|
};
|
|
window.addEventListener('keydown', handleEscapeKey);
|
|
if (options.catchRuntimeError) {
|
|
var handleError_1 = function (error, fallbackMessage) {
|
|
var errorObject = error instanceof Error
|
|
? error
|
|
: // @ts-expect-error error options
|
|
new Error(error || fallbackMessage, { cause: error });
|
|
var shouldDisplay = typeof options.catchRuntimeError === 'function'
|
|
? options.catchRuntimeError(errorObject)
|
|
: true;
|
|
if (shouldDisplay) {
|
|
overlayService.send({
|
|
type: 'RUNTIME_ERROR',
|
|
messages: [
|
|
{
|
|
message: errorObject.message,
|
|
stack: parseErrorToStacks(errorObject),
|
|
},
|
|
],
|
|
});
|
|
}
|
|
};
|
|
listenToRuntimeError(function (errorEvent) {
|
|
// error property may be empty in older browser like IE
|
|
var error = errorEvent.error, message = errorEvent.message;
|
|
if (!error && !message) {
|
|
return;
|
|
}
|
|
// if error stack indicates a React error boundary caught the error, do not show overlay.
|
|
if (error &&
|
|
error.stack &&
|
|
error.stack.includes('invokeGuardedCallbackDev')) {
|
|
return;
|
|
}
|
|
handleError_1(error, message);
|
|
});
|
|
listenToUnhandledRejection(function (promiseRejectionEvent) {
|
|
var reason = promiseRejectionEvent.reason;
|
|
handleError_1(reason, 'Unknown promise rejection reason');
|
|
});
|
|
}
|
|
return overlayService;
|
|
};
|
|
export { createOverlay, formatProblem };
|
|
|