diff --git a/packages/core/src/client/hmr.ts b/packages/core/src/client/hmr.ts index fd96c62ca3..21655ef122 100644 --- a/packages/core/src/client/hmr.ts +++ b/packages/core/src/client/hmr.ts @@ -41,11 +41,11 @@ function clearOutdatedErrors() { } } -let createOverlay: undefined | ((overlay: string, errors: string[]) => void); +let createOverlay: undefined | ((html: string) => void); let clearOverlay: undefined | (() => void); export const registerOverlay = ( - createFn: (overlay: string, errors: string[]) => void, + createFn: (html: string) => void, clearFn: () => void, ): void => { createOverlay = createFn; @@ -91,15 +91,7 @@ function handleWarnings({ text }: { text: string[] }) { } // Compilation with errors (e.g. syntax error or missing modules). -function handleErrors({ - text, - html, - overlay, -}: { - text: string[]; - html: string[]; - overlay: string; -}) { +function handleErrors({ text, html }: { text: string[]; html: string }) { clearOutdatedErrors(); isFirstCompilation = false; @@ -111,7 +103,7 @@ function handleErrors({ } if (createOverlay) { - createOverlay(overlay, html); + createOverlay(html); } } diff --git a/packages/core/src/client/overlay.ts b/packages/core/src/client/overlay.ts index a0205cf51a..b6ad835a9c 100644 --- a/packages/core/src/client/overlay.ts +++ b/packages/core/src/client/overlay.ts @@ -1,40 +1,12 @@ import { registerOverlay } from './hmr'; -function linkedText(root: ShadowRoot, selector: string, text: string): void { - const el = root.querySelector(selector)!; - const fileRegex = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g; - - let curIndex = 0; - let match = fileRegex.exec(text); - while (match !== null) { - const { 0: file, index } = match; - if (index != null) { - const frag = text.slice(curIndex, index); - el.insertAdjacentHTML('beforeend', frag); - const link = document.createElement('a'); - link.textContent = file; - link.className = 'file-link'; - - link.onclick = () => { - fetch(`/__open-in-editor?file=${encodeURIComponent(file)}`); - }; - el.appendChild(link); - curIndex += frag.length + file.length; - } - match = fileRegex.exec(text); - } - - const frag = text.slice(curIndex); - el.insertAdjacentHTML('beforeend', frag); -} - const { HTMLElement = class {} as typeof globalThis.HTMLElement, customElements, } = typeof window !== 'undefined' ? window : globalThis; class ErrorOverlay extends HTMLElement { - constructor(overlay: string, errors: string[]) { + constructor(html: string) { super(); if (!this.attachShadow) { @@ -45,13 +17,18 @@ class ErrorOverlay extends HTMLElement { } const root = this.attachShadow({ mode: 'open' }); - root.innerHTML = overlay; + root.innerHTML = html; - linkedText(root, '.content', errors.join('\n\n').trim()); - root.querySelector('.close')?.addEventListener('click', this.close); + root.querySelector('.close')!.addEventListener('click', this.close); // close overlay when click outside this.addEventListener('click', this.close); root.querySelector('.container')!.addEventListener('click', (e) => { + if (e.target) { + const { file } = (e.target as HTMLLinkElement).dataset; + if (file) { + fetch(`/__open-in-editor?file=${encodeURIComponent(file)}`); + } + } e.stopPropagation(); }); @@ -84,9 +61,9 @@ if (customElements && !customElements.get(overlayId)) { customElements.define(overlayId, ErrorOverlay); } -function createOverlay(overlay: string, errors: string[]) { +function createOverlay(html: string) { clearOverlay(); - document.body.appendChild(new ErrorOverlay(overlay, errors)); + document.body.appendChild(new ErrorOverlay(html)); } function clearOverlay() { diff --git a/packages/core/src/server/overlay.ts b/packages/core/src/server/overlay.ts new file mode 100644 index 0000000000..fcce8e87a7 --- /dev/null +++ b/packages/core/src/server/overlay.ts @@ -0,0 +1,144 @@ +import ansiHTML from './ansiHTML'; +import { escapeHtml } from './helper'; + +export function convertLinksInHtml(text: string): string { + const fileRegex = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g; + + return text.replace(fileRegex, (file) => { + // If the file contains ``, it means the file path contains ANSI codes. + // We need to move the `` to the end of the file path. + const hasClosingSpan = file.includes('') && !file.includes('', '') : file; + + return `${filePath}${ + hasClosingSpan ? '' : '' + }`; + }); +} + +// HTML template for error overlay +export function genOverlayHTML(errors: string[]) { + const htmlItems = errors.map((item) => + convertLinksInHtml(ansiHTML(escapeHtml(item))), + ); + return ` + + +
Build failed
+${htmlItems.join('\n\n').trim()}
+
+ Build failed
- - -