|
|
const RuntimeErrorFooter = require('./components/RuntimeErrorFooter.js'); |
|
|
const RuntimeErrorHeader = require('./components/RuntimeErrorHeader.js'); |
|
|
const CompileErrorContainer = require('./containers/CompileErrorContainer.js'); |
|
|
const RuntimeErrorContainer = require('./containers/RuntimeErrorContainer.js'); |
|
|
const theme = require('./theme.js'); |
|
|
const utils = require('./utils.js'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let iframeRoot = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let rootDocument = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let root = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let scheduledRenderFn = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let currentCompileErrorMessage = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let currentRuntimeErrorIndex = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let currentRuntimeErrors = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let currentMode = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function IframeRoot(document, root, props) { |
|
|
const iframe = document.createElement('iframe'); |
|
|
iframe.id = 'react-refresh-overlay'; |
|
|
iframe.src = 'about:blank'; |
|
|
|
|
|
iframe.style.border = 'none'; |
|
|
iframe.style.height = '100%'; |
|
|
iframe.style.left = '0'; |
|
|
iframe.style.minHeight = '100vh'; |
|
|
iframe.style.minHeight = '-webkit-fill-available'; |
|
|
iframe.style.position = 'fixed'; |
|
|
iframe.style.top = '0'; |
|
|
iframe.style.width = '100vw'; |
|
|
iframe.style.zIndex = '2147483647'; |
|
|
iframe.addEventListener('load', function onLoad() { |
|
|
|
|
|
iframe.contentDocument.body.style.margin = '0'; |
|
|
props.onIframeLoad(); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return iframe; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function OverlayRoot(document, root) { |
|
|
const div = document.createElement('div'); |
|
|
div.id = 'react-refresh-overlay-error'; |
|
|
|
|
|
|
|
|
div.style.backgroundColor = '#' + theme.grey; |
|
|
div.style.boxSizing = 'border-box'; |
|
|
div.style.color = '#' + theme.white; |
|
|
div.style.fontFamily = [ |
|
|
'-apple-system', |
|
|
'BlinkMacSystemFont', |
|
|
'"Segoe UI"', |
|
|
'"Helvetica Neue"', |
|
|
'Helvetica', |
|
|
'Arial', |
|
|
'sans-serif', |
|
|
'"Apple Color Emoji"', |
|
|
'"Segoe UI Emoji"', |
|
|
'Segoe UI Symbol', |
|
|
].join(', '); |
|
|
div.style.fontSize = '0.875rem'; |
|
|
div.style.height = '100%'; |
|
|
div.style.lineHeight = '1.3'; |
|
|
div.style.overflow = 'auto'; |
|
|
div.style.padding = '1rem 1.5rem 0'; |
|
|
div.style.paddingTop = 'max(1rem, env(safe-area-inset-top))'; |
|
|
div.style.paddingRight = 'max(1.5rem, env(safe-area-inset-right))'; |
|
|
div.style.paddingBottom = 'env(safe-area-inset-bottom)'; |
|
|
div.style.paddingLeft = 'max(1.5rem, env(safe-area-inset-left))'; |
|
|
div.style.width = '100vw'; |
|
|
|
|
|
root.appendChild(div); |
|
|
return div; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function ensureRootExists(renderFn) { |
|
|
if (root) { |
|
|
|
|
|
renderFn(); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
scheduledRenderFn = renderFn; |
|
|
|
|
|
if (iframeRoot) { |
|
|
|
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
iframeRoot = IframeRoot(document, document.body, { |
|
|
onIframeLoad: function onIframeLoad() { |
|
|
rootDocument = iframeRoot.contentDocument; |
|
|
root = OverlayRoot(rootDocument, rootDocument.body); |
|
|
scheduledRenderFn(); |
|
|
}, |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.body.appendChild(iframeRoot); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function render() { |
|
|
ensureRootExists(function () { |
|
|
const currentFocus = rootDocument.activeElement; |
|
|
let currentFocusId; |
|
|
if (currentFocus.localName === 'button' && currentFocus.id) { |
|
|
currentFocusId = currentFocus.id; |
|
|
} |
|
|
|
|
|
utils.removeAllChildren(root); |
|
|
|
|
|
if (currentCompileErrorMessage) { |
|
|
currentMode = 'compileError'; |
|
|
|
|
|
CompileErrorContainer(rootDocument, root, { |
|
|
errorMessage: currentCompileErrorMessage, |
|
|
}); |
|
|
} else if (currentRuntimeErrors.length) { |
|
|
currentMode = 'runtimeError'; |
|
|
|
|
|
RuntimeErrorHeader(rootDocument, root, { |
|
|
currentErrorIndex: currentRuntimeErrorIndex, |
|
|
totalErrors: currentRuntimeErrors.length, |
|
|
}); |
|
|
RuntimeErrorContainer(rootDocument, root, { |
|
|
currentError: currentRuntimeErrors[currentRuntimeErrorIndex], |
|
|
}); |
|
|
RuntimeErrorFooter(rootDocument, root, { |
|
|
initialFocus: currentFocusId, |
|
|
multiple: currentRuntimeErrors.length > 1, |
|
|
onClickCloseButton: function onClose() { |
|
|
clearRuntimeErrors(); |
|
|
}, |
|
|
onClickNextButton: function onNext() { |
|
|
if (currentRuntimeErrorIndex === currentRuntimeErrors.length - 1) { |
|
|
return; |
|
|
} |
|
|
currentRuntimeErrorIndex += 1; |
|
|
ensureRootExists(render); |
|
|
}, |
|
|
onClickPrevButton: function onPrev() { |
|
|
if (currentRuntimeErrorIndex === 0) { |
|
|
return; |
|
|
} |
|
|
currentRuntimeErrorIndex -= 1; |
|
|
ensureRootExists(render); |
|
|
}, |
|
|
}); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function cleanup() { |
|
|
|
|
|
document.body.removeChild(iframeRoot); |
|
|
scheduledRenderFn = null; |
|
|
root = null; |
|
|
iframeRoot = null; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function clearCompileError() { |
|
|
if (!root || currentMode !== 'compileError') { |
|
|
return; |
|
|
} |
|
|
|
|
|
currentCompileErrorMessage = ''; |
|
|
currentMode = null; |
|
|
cleanup(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function clearRuntimeErrors(dismissOverlay) { |
|
|
if (!root || currentMode !== 'runtimeError') { |
|
|
return; |
|
|
} |
|
|
|
|
|
currentRuntimeErrorIndex = 0; |
|
|
currentRuntimeErrors = []; |
|
|
|
|
|
if (typeof dismissOverlay === 'undefined' || dismissOverlay) { |
|
|
currentMode = null; |
|
|
cleanup(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showCompileError(message) { |
|
|
if (!message) { |
|
|
return; |
|
|
} |
|
|
|
|
|
currentCompileErrorMessage = message; |
|
|
|
|
|
render(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showRuntimeErrors(errors) { |
|
|
if (!errors || !errors.length) { |
|
|
return; |
|
|
} |
|
|
|
|
|
currentRuntimeErrors = errors; |
|
|
|
|
|
render(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const debouncedShowRuntimeErrors = utils.debounce(showRuntimeErrors, 30); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isWebpackCompileError(error) { |
|
|
return /Module [A-z ]+\(from/.test(error.message) || /Cannot find module/.test(error.message); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handleRuntimeError(error) { |
|
|
if (error && !isWebpackCompileError(error) && currentRuntimeErrors.indexOf(error) === -1) { |
|
|
currentRuntimeErrors = currentRuntimeErrors.concat(error); |
|
|
} |
|
|
debouncedShowRuntimeErrors(currentRuntimeErrors); |
|
|
} |
|
|
|
|
|
module.exports = Object.freeze({ |
|
|
clearCompileError: clearCompileError, |
|
|
clearRuntimeErrors: clearRuntimeErrors, |
|
|
handleRuntimeError: handleRuntimeError, |
|
|
showCompileError: showCompileError, |
|
|
showRuntimeErrors: showRuntimeErrors, |
|
|
}); |
|
|
|