Skip to content

Commit 34142a8

Browse files
committed
refactor
1 parent a7d459b commit 34142a8

3 files changed

Lines changed: 161 additions & 144 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import ansiHTML from './ansiHTML';
2+
import { escapeHtml } from './helper';
3+
4+
export function convertLinksInHtml(text: string): string {
5+
const fileRegex = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g;
6+
7+
return text.replace(fileRegex, (file) => {
8+
// If the file contains `</span>`, it means the file path contains ANSI codes.
9+
// We need to move the `</span>` to the end of the file path.
10+
const hasClosingSpan = file.includes('</span>') && !file.includes('<span');
11+
const filePath = hasClosingSpan ? file.replace('</span>', '') : file;
12+
13+
return `<a class="file-link" data-file="${filePath}">${filePath}</a>${
14+
hasClosingSpan ? '</span>' : ''
15+
}`;
16+
});
17+
}
18+
19+
// HTML template for error overlay
20+
export function genOverlayHTML(errors: string[]) {
21+
const htmlItems = errors.map((item) =>
22+
convertLinksInHtml(ansiHTML(escapeHtml(item))),
23+
);
24+
return `
25+
<style>
26+
.root {
27+
position: fixed;
28+
z-index: 9999;
29+
top: 0;
30+
left: 0;
31+
width: 100%;
32+
height: 100%;
33+
overflow-y: scroll;
34+
margin: 0;
35+
background: rgba(0, 0, 0, 0.66);
36+
cursor: pointer;
37+
}
38+
.container {
39+
font-family: Menlo, Consolas, monospace;
40+
line-height: 1.6;
41+
width: 960px;
42+
max-width: 85%;
43+
color: #d8d8d8;
44+
margin: 32px auto;
45+
padding: 32px 40px;
46+
position: relative;
47+
background: #181818;
48+
border-radius: 24px;
49+
box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
50+
overflow: hidden;
51+
direction: ltr;
52+
text-align: left;
53+
box-sizing: border-box;
54+
cursor: default;
55+
}
56+
.title {
57+
margin: 0 0 20px;
58+
padding-bottom: 12px;
59+
font-size: 17px;
60+
font-weight: 600;
61+
color: #fb6a6a;
62+
border-bottom: 2px solid rgba(252,94,94,.66);
63+
}
64+
.content {
65+
margin: 0;
66+
font-size: 14px;
67+
font-family: inherit;
68+
overflow-x: scroll;
69+
scrollbar-width: none;
70+
}
71+
.content::-webkit-scrollbar {
72+
display: none;
73+
}
74+
.file-link {
75+
cursor: pointer;
76+
color: #6eecf7;
77+
text-decoration: underline;
78+
text-underline-offset: 3px;
79+
&:hover {
80+
opacity: 0.8;
81+
}
82+
&:active {
83+
opacity: 0.6;
84+
}
85+
}
86+
.close {
87+
position: absolute;
88+
top: 27px;
89+
right: 32px;
90+
width: 32px;
91+
height: 32px;
92+
cursor: pointer;
93+
}
94+
.close:hover {
95+
opacity: 0.8;
96+
}
97+
.close:active {
98+
opacity: 0.6;
99+
}
100+
.close:before,
101+
.close:after {
102+
position: absolute;
103+
left: 16px;
104+
top: 8px;
105+
content: ' ';
106+
height: 18px;
107+
width: 2px;
108+
border-radius: 4px;
109+
background-color: #b8b8b8;
110+
}
111+
.close:before {
112+
transform: rotate(45deg);
113+
}
114+
.close:after {
115+
transform: rotate(-45deg);
116+
}
117+
.footer {
118+
font-size: 12px;
119+
color: #7e6a92;
120+
margin-top: 20px;
121+
padding-top: 12px;
122+
border-top: 2px solid rgba(126,106,146,.6);
123+
}
124+
.footer p {
125+
margin: 4px 0 0;
126+
}
127+
.footer span {
128+
color: #a88dc3;
129+
}
130+
</style>
131+
132+
<div class="root">
133+
<div class="container">
134+
<div class="close"></div>
135+
<p class="title">Build failed</p>
136+
<pre class="content">${htmlItems.join('\n\n').trim()}</pre>
137+
<footer class="footer">
138+
<p><span>Fix error</span>, click outside, or press Esc to close the overlay.</p>
139+
<p>Disable overlay by setting Rsbuild's <span>dev.client.overlay</span> config to false.<p>
140+
</footer>
141+
</div>
142+
</div>
143+
`;
144+
}

packages/core/src/server/socketServer.ts

Lines changed: 2 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -10,155 +10,13 @@ import {
1010
import { formatStatsMessages } from '../helpers/format';
1111
import { logger } from '../logger';
1212
import type { DevConfig, Rspack } from '../types';
13-
import ansiHTML from './ansiHTML';
14-
import { escapeHtml, getCompilationId } from './helper';
13+
import { getCompilationId } from './helper';
14+
import { genOverlayHTML } from './overlay';
1515

1616
interface ExtWebSocket extends Ws {
1717
isAlive: boolean;
1818
}
1919

20-
function convertLinksInHtml(text: string): string {
21-
const fileRegex = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g;
22-
23-
return text.replace(fileRegex, (file) => {
24-
// If the file contains `</span>`, it means the file path contains ANSI codes.
25-
// We need to move the `</span>` to the end of the file path.
26-
const hasClosingSpan = file.includes('</span>') && !file.includes('<span');
27-
const filePath = hasClosingSpan ? file.replace('</span>', '') : file;
28-
29-
return `<a class="file-link" data-file="${filePath}">${filePath}</a>${
30-
hasClosingSpan ? '</span>' : ''
31-
}`;
32-
});
33-
}
34-
35-
// HTML template for error overlay
36-
function genOverlayHTML(errors: string[]) {
37-
const htmlItems = errors.map((item) =>
38-
convertLinksInHtml(ansiHTML(escapeHtml(item))),
39-
);
40-
return `
41-
<style>
42-
.root {
43-
position: fixed;
44-
z-index: 9999;
45-
top: 0;
46-
left: 0;
47-
width: 100%;
48-
height: 100%;
49-
overflow-y: scroll;
50-
margin: 0;
51-
background: rgba(0, 0, 0, 0.66);
52-
cursor: pointer;
53-
}
54-
.container {
55-
font-family: Menlo, Consolas, monospace;
56-
line-height: 1.6;
57-
width: 960px;
58-
max-width: 85%;
59-
color: #d8d8d8;
60-
margin: 32px auto;
61-
padding: 32px 40px;
62-
position: relative;
63-
background: #181818;
64-
border-radius: 24px;
65-
box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
66-
overflow: hidden;
67-
direction: ltr;
68-
text-align: left;
69-
box-sizing: border-box;
70-
cursor: default;
71-
}
72-
.title {
73-
margin: 0 0 20px;
74-
padding-bottom: 12px;
75-
font-size: 17px;
76-
font-weight: 600;
77-
color: #fb6a6a;
78-
border-bottom: 2px solid rgba(252,94,94,.66);
79-
}
80-
.content {
81-
margin: 0;
82-
font-size: 14px;
83-
font-family: inherit;
84-
overflow-x: scroll;
85-
scrollbar-width: none;
86-
}
87-
.content::-webkit-scrollbar {
88-
display: none;
89-
}
90-
.file-link {
91-
cursor: pointer;
92-
color: #6eecf7;
93-
text-decoration: underline;
94-
text-underline-offset: 3px;
95-
&:hover {
96-
opacity: 0.8;
97-
}
98-
&:active {
99-
opacity: 0.6;
100-
}
101-
}
102-
.close {
103-
position: absolute;
104-
top: 27px;
105-
right: 32px;
106-
width: 32px;
107-
height: 32px;
108-
cursor: pointer;
109-
}
110-
.close:hover {
111-
opacity: 0.8;
112-
}
113-
.close:active {
114-
opacity: 0.6;
115-
}
116-
.close:before,
117-
.close:after {
118-
position: absolute;
119-
left: 16px;
120-
top: 8px;
121-
content: ' ';
122-
height: 18px;
123-
width: 2px;
124-
border-radius: 4px;
125-
background-color: #b8b8b8;
126-
}
127-
.close:before {
128-
transform: rotate(45deg);
129-
}
130-
.close:after {
131-
transform: rotate(-45deg);
132-
}
133-
.footer {
134-
font-size: 12px;
135-
color: #7e6a92;
136-
margin-top: 20px;
137-
padding-top: 12px;
138-
border-top: 2px solid rgba(126,106,146,.6);
139-
}
140-
.footer p {
141-
margin: 4px 0 0;
142-
}
143-
.footer span {
144-
color: #a88dc3;
145-
}
146-
</style>
147-
148-
<div class="root">
149-
<div class="container">
150-
<div class="close"></div>
151-
<p class="title">Build failed</p>
152-
<pre class="content">${htmlItems.join('\n\n').trim()}</pre>
153-
<footer class="footer">
154-
<p><span>Fix error</span>, click outside, or press Esc to close the overlay.</p>
155-
<p>Disable overlay by setting Rsbuild's <span>dev.client.overlay</span> config to false.<p>
156-
</footer>
157-
</div>
158-
</div>
159-
`;
160-
}
161-
16220
function isEqualSet(a: Set<string>, b: Set<string>): boolean {
16321
return a.size === b.size && [...a].every((value) => b.has(value));
16422
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ansiHTML } from '../src/server/ansiHTML';
2+
import { convertLinksInHtml } from '../src/server/overlay';
23

34
describe('ansiHTML', () => {
45
it('should convert ANSI color codes to HTML', () => {
@@ -113,3 +114,17 @@ describe('ansiHTML', () => {
113114
expect(ansiHTML(input)).toEqual(expected);
114115
});
115116
});
117+
118+
describe('convertLinksInHtml', () => {
119+
it('should convert file path with ANSI color codes to HTML', () => {
120+
const input1 = '[\u001b[36;1;4m/path/to/src/index.js\u001b[0m:4:1]\n';
121+
const expected1 =
122+
'[<span style="color:#6eecf7;font-weight:bold;text-decoration:underline;text-underline-offset:3px;"><a class="file-link" data-file="/path/to/src/index.js:4:1">/path/to/src/index.js:4:1</a></span>]\n';
123+
expect(convertLinksInHtml(ansiHTML(input1))).toEqual(expected1);
124+
125+
const input2 = '[\u001b[36;1;4m/path/to/src/index.js:4:1\u001b[0m]\n';
126+
const expected2 =
127+
'[<span style="color:#6eecf7;font-weight:bold;text-decoration:underline;text-underline-offset:3px;"><a class="file-link" data-file="/path/to/src/index.js:4:1">/path/to/src/index.js:4:1</a></span>]\n';
128+
expect(convertLinksInHtml(ansiHTML(input2))).toEqual(expected2);
129+
});
130+
});

0 commit comments

Comments
 (0)