Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions packages/core/src/client/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ function clearOutdatedErrors() {
}
}

let createOverlay: undefined | ((err: string[]) => void);
let createOverlay: undefined | ((overlay: string, errors: string[]) => void);
let clearOverlay: undefined | (() => void);

export const registerOverlay = (
createFn: (err: string[]) => void,
createFn: (overlay: string, errors: string[]) => void,
clearFn: () => void,
): void => {
createOverlay = createFn;
Expand Down Expand Up @@ -91,7 +91,15 @@ function handleWarnings({ text }: { text: string[] }) {
}

// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors({ text, html }: { text: string[]; html: string[] }) {
function handleErrors({
text,
html,
overlay,
}: {
text: string[];
html: string[];
overlay: string;
}) {
clearOutdatedErrors();

isFirstCompilation = false;
Expand All @@ -103,7 +111,7 @@ function handleErrors({ text, html }: { text: string[]; html: string[] }) {
}

if (createOverlay) {
createOverlay(html);
createOverlay(overlay, html);
}
}

Expand Down
135 changes: 5 additions & 130 deletions packages/core/src/client/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,134 +28,13 @@ function linkedText(root: ShadowRoot, selector: string, text: string): void {
el.insertAdjacentHTML('beforeend', frag);
}

const overlayTemplate = `
<style>
.root {
position: fixed;
z-index: 9999;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow-y: scroll;
margin: 0;
background: rgba(0, 0, 0, 0.66);
cursor: pointer;
}
.container {
font-family: Menlo, Consolas, monospace;
line-height: 1.6;
width: 960px;
max-width: 85%;
color: #d8d8d8;
margin: 32px auto;
padding: 32px 40px;
position: relative;
background: #181818;
border-radius: 24px;
box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
overflow: hidden;
direction: ltr;
text-align: left;
box-sizing: border-box;
cursor: default;
}
.title {
margin: 0 0 20px;
padding-bottom: 12px;
font-size: 17px;
font-weight: 600;
color: #fb6a6a;
border-bottom: 2px solid rgba(252,94,94,.66);
}
.content {
margin: 0;
font-size: 14px;
font-family: inherit;
overflow-x: scroll;
scrollbar-width: none;
}
.content::-webkit-scrollbar {
display: none;
}
.file-link {
cursor: pointer;
color: #6eecf7;
text-decoration: underline;
text-underline-offset: 3px;
&:hover {
opacity: 0.8;
}
&:active {
opacity: 0.6;
}
}
.close {
position: absolute;
top: 27px;
right: 32px;
width: 32px;
height: 32px;
cursor: pointer;
}
.close:hover {
opacity: 0.8;
}
.close:active {
opacity: 0.6;
}
.close:before,
.close:after {
position: absolute;
left: 16px;
top: 8px;
content: ' ';
height: 18px;
width: 2px;
border-radius: 4px;
background-color: #b8b8b8;
}
.close:before {
transform: rotate(45deg);
}
.close:after {
transform: rotate(-45deg);
}
.footer {
font-size: 12px;
color: #7e6a92;
margin-top: 20px;
padding-top: 12px;
border-top: 2px solid rgba(126,106,146,.6);
}
.footer p {
margin: 4px 0 0;
}
.footer span {
color: #a88dc3;
}
</style>

<div class="root">
<div class="container">
<div class="close"></div>
<p class="title">Build failed</p>
<pre class="content"></pre>
<footer class="footer">
<p><span>Fix error</span>, click outside, or press Esc to close the overlay.</p>
<p>Disable overlay by setting Rsbuild's <span>dev.client.overlay</span> config to false.<p>
</footer>
</div>
</div>
`;

const {
HTMLElement = class {} as typeof globalThis.HTMLElement,
customElements,
} = typeof window !== 'undefined' ? window : globalThis;

class ErrorOverlay extends HTMLElement {
constructor(message: string[]) {
constructor(overlay: string, errors: string[]) {
super();

if (!this.attachShadow) {
Expand All @@ -166,15 +45,12 @@ class ErrorOverlay extends HTMLElement {
}

const root = this.attachShadow({ mode: 'open' });
root.innerHTML = overlayTemplate;

linkedText(root, '.content', message.join('\n\n').trim());
root.innerHTML = overlay;

linkedText(root, '.content', errors.join('\n\n').trim());
root.querySelector('.close')?.addEventListener('click', this.close);

// close overlay when click outside
this.addEventListener('click', this.close);

root.querySelector('.container')!.addEventListener('click', (e) => {
e.stopPropagation();
});
Expand All @@ -185,7 +61,6 @@ class ErrorOverlay extends HTMLElement {
}
document.removeEventListener('keydown', onEscKeydown);
};

document.addEventListener('keydown', onEscKeydown);
}

Expand All @@ -209,9 +84,9 @@ if (customElements && !customElements.get(overlayId)) {
customElements.define(overlayId, ErrorOverlay);
}

function createOverlay(err: string[]) {
function createOverlay(overlay: string, errors: string[]) {
clearOverlay();
document.body.appendChild(new ErrorOverlay(err));
document.body.appendChild(new ErrorOverlay(overlay, errors));
}

function clearOverlay() {
Expand Down
123 changes: 123 additions & 0 deletions packages/core/src/server/socketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,128 @@ interface ExtWebSocket extends Ws {
isAlive: boolean;
}

// HTML template for error overlay
const overlayTemplate = `
<style>
.root {
position: fixed;
z-index: 9999;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow-y: scroll;
margin: 0;
background: rgba(0, 0, 0, 0.66);
cursor: pointer;
}
.container {
font-family: Menlo, Consolas, monospace;
line-height: 1.6;
width: 960px;
max-width: 85%;
color: #d8d8d8;
margin: 32px auto;
padding: 32px 40px;
position: relative;
background: #181818;
border-radius: 24px;
box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
overflow: hidden;
direction: ltr;
text-align: left;
box-sizing: border-box;
cursor: default;
}
.title {
margin: 0 0 20px;
padding-bottom: 12px;
font-size: 17px;
font-weight: 600;
color: #fb6a6a;
border-bottom: 2px solid rgba(252,94,94,.66);
}
.content {
margin: 0;
font-size: 14px;
font-family: inherit;
overflow-x: scroll;
scrollbar-width: none;
}
.content::-webkit-scrollbar {
display: none;
}
.file-link {
cursor: pointer;
color: #6eecf7;
text-decoration: underline;
text-underline-offset: 3px;
&:hover {
opacity: 0.8;
}
&:active {
opacity: 0.6;
}
}
.close {
position: absolute;
top: 27px;
right: 32px;
width: 32px;
height: 32px;
cursor: pointer;
}
.close:hover {
opacity: 0.8;
}
.close:active {
opacity: 0.6;
}
.close:before,
.close:after {
position: absolute;
left: 16px;
top: 8px;
content: ' ';
height: 18px;
width: 2px;
border-radius: 4px;
background-color: #b8b8b8;
}
.close:before {
transform: rotate(45deg);
}
.close:after {
transform: rotate(-45deg);
}
.footer {
font-size: 12px;
color: #7e6a92;
margin-top: 20px;
padding-top: 12px;
border-top: 2px solid rgba(126,106,146,.6);
}
.footer p {
margin: 4px 0 0;
}
.footer span {
color: #a88dc3;
}
</style>

<div class="root">
<div class="container">
<div class="close"></div>
<p class="title">Build failed</p>
<pre class="content"></pre>
<footer class="footer">
<p><span>Fix error</span>, click outside, or press Esc to close the overlay.</p>
<p>Disable overlay by setting Rsbuild's <span>dev.client.overlay</span> config to false.<p>
</footer>
</div>
</div>
`;

function isEqualSet(a: Set<string>, b: Set<string>): boolean {
if (a.size !== b.size) {
return false;
Expand Down Expand Up @@ -297,6 +419,7 @@ export class SocketServer {
data: {
text: formattedErrors,
html: formattedErrors.map((item) => ansiHTML(escapeHtml(item))),
overlay: overlayTemplate,
},
});
}
Expand Down
Loading