UNPKG

15 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <title></title>
8 </head>
9
10 <style>
11 body {
12 overflow-wrap: break-word;
13 font-size: 18px;
14 }
15 h3 {
16 font-family: system-ui, -apple-system, BlinkMacSystemFont,
17 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
18 'Helvetica Neue', sans-serif;
19 font-weight: normal;
20 }
21 </style>
22
23 <body></body>
24 <script type="module">
25 import {
26 createSignal,
27 createResource,
28 onMount,
29 createEffect,
30 } from 'https://esm.sh/solid-js';
31
32 import html from 'https://esm.sh/solid-js/html';
33 import * as echarts from 'https://cdn.jsdelivr.net/npm/echarts@5.3.3/dist/echarts.esm.min.js';
34 const getFontData = (nameTable) =>
35 Object.fromEntries(
36 Object.entries(nameTable).map(([key, val]) => {
37 return [key, typeof val === 'string' ? val : val.en];
38 })
39 );
40 function _formatBytes(bytes, decimals = 2) {
41 if (bytes === 0) return '0 Bytes';
42 const k = 1024;
43 const dm = decimals < 0 ? 0 : decimals;
44 const sizes = [
45 'Bytes',
46 'KB',
47 'MB',
48 'GB',
49 'TB',
50 'PB',
51 'EB',
52 'ZB',
53 'YB',
54 ];
55 const i = Math.floor(Math.log(bytes) / Math.log(k));
56 return (
57 parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) +
58 ' ' +
59 sizes[i]
60 );
61 }
62 const range = [
63 ['基本汉字', 0x4e00, 0x9fa5],
64 ['基本汉字补充', 0x9fa6, 0x9fff],
65 ['扩展A', 0x3400, 0x4dbf],
66 ['扩展B', 0x20000, 0x2a6df],
67 ['扩展C', 0x2a700, 0x2b738],
68 ['扩展D', 0x2b740, 0x2b81d],
69 ['扩展E', 0x2b820, 0x2cea1],
70 ['扩展F', 0x2ceb0, 0x2ebe0],
71 ['扩展G', 0x30000, 0x3134a],
72 ['康熙部首', 0x2f00, 0x2fd5],
73 ['部首扩展', 0x2e80, 0x2ef3],
74 ['兼容汉字', 0xf900, 0xfad9],
75 ['兼容扩展', 0x2f800, 0x2fa1d],
76 ['PUA(GBK)部件', 0xe815, 0xe86f],
77 ['部件扩展', 0xe400, 0xe5e8],
78 ['PUA增补', 0xe600, 0xe6cf],
79 ['汉字笔画', 0x31c0, 0x31e3],
80 ['汉字结构', 0x2ff0, 0x2ffb],
81 ['汉语注音', 0x3105, 0x312f],
82 ['注音扩展', 0x31a0, 0x31ba],
83 ['〇', 0x3007, 0x3007],
84 ];
85 import { UnicodeRange } from 'https://esm.sh/@japont/unicode-range@1.0.0';
86 const RangeAnalyze = (data) => {
87 const total = data.reduce((col, cur) => {
88 if (cur.chars.startsWith('U+')) {
89 return (
90 col +
91 String.fromCharCode(
92 ...UnicodeRange.parse(cur.chars.split(','))
93 )
94 );
95 }
96 return col + cur.chars;
97 }, '');
98 const result = range.map(([name, min, max]) => {
99 let exist = '';
100 let voids = '';
101 for (let i = min; i <= max; i++) {
102 const char = String.fromCharCode(i);
103 const isExist = total.includes(char);
104 if (isExist) {
105 exist += char;
106 } else {
107 voids += char;
108 }
109 }
110 return [name, exist, voids];
111 });
112 return html`
113 <table style="width:80%;margin:auto;padding:1rem">
114 <thead>
115 <tr>
116 <th>位置</th>
117 <th>存在</th>
118 <th>不存在</th>
119 <th>覆盖率</th>
120 </tr>
121 </thead>
122 ${result.map(([name, exist, voids]) => {
123 const coverage =
124 (exist.length * 100) /
125 (exist.length + voids.length);
126 return html`
127 <tr>
128 <td>${name}</td>
129 <td>${exist.length}</td>
130 <td>${voids.length}</td>
131 <td>${coverage.toFixed(2)}%</td>
132 </tr>
133 `;
134 })}
135 </table>
136 `;
137 };
138
139 const TimeAnalyze = (record, message) => {
140 record.pop(); // 最后一个记录是没有用的
141 const total = record.reduce(
142 (col, cur) => col + cur.end - cur.start,
143 0
144 );
145 let chartDom;
146 // console.log(record, total);
147
148 onMount(() => {
149 // 问就是渲染代价太大,等 1000ms 让浏览器冷静一下
150 setTimeout(() => {
151 let myChart = echarts.init(chartDom);
152 let option = {
153 tooltip: {
154 trigger: 'axis',
155 axisPointer: {
156 type: 'shadow',
157 },
158 },
159 title: {
160 text: '打包时间分布图',
161 subtext: `时间为 ms; 总时间 ${total} ms;\n${message.fontFamily}`,
162 },
163 legend: {
164 top: '15%',
165 },
166 grid: {
167 left: '10%',
168 right: '10%',
169 bottom: '30%',
170 top: '30%',
171 },
172 xAxis: {
173 type: 'value',
174 },
175 yAxis: {
176 type: 'category',
177 data: ['时间轴'],
178 },
179 series: record.map((i) => {
180 return {
181 name: i.name,
182 type: 'bar',
183 stack: 'total',
184 label: {
185 show: true,
186 },
187 emphasis: {
188 focus: 'series',
189 },
190 data: [
191 parseFloat((i.end - i.start).toFixed(1)),
192 ],
193 };
194 }),
195 };
196
197 option && myChart.setOption(option);
198 }, 1500);
199 });
200 return html`<div
201 ref=${function (dom) {
202 chartDom = dom;
203 }}
204 style="width: 600px;height:400px;margin:auto"
205 ></div>`;
206 };
207 const DataAnalyze = (data, message) => {
208 const total = data.reduce((col, cur) => col + cur.size, 0);
209 // console.log(data, total);
210 let chartDom;
211 onMount(() => {
212 // 问就是渲染代价太大,等 1000ms 让浏览器冷静一下
213 setTimeout(() => {
214 let myChart = echarts.init(chartDom);
215 let option = {
216 tooltip: {
217 trigger: 'item',
218 formatter(data) {
219 return (
220 `第 ${data.dataIndex + 1} 分包\n` +
221 data.data.name +
222 '\n' +
223 _formatBytes(data.data.value)
224 );
225 },
226 },
227
228 title: {
229 text: message.fontFamily,
230 subtext: `总共 ${
231 data.length
232 } 分包; 总大小 ${_formatBytes(
233 total
234 )} 点击跳转查看;`,
235 left: 'center',
236 },
237 series: [
238 {
239 name: '分包信息',
240 type: 'pie',
241 radius: ['40%', '70%'],
242 avoidLabelOverlap: false,
243 itemStyle: {
244 borderRadius: 10,
245 borderColor: '#fff',
246 borderWidth: 2,
247 },
248
249 emphasis: {
250 label: {
251 show: true,
252 fontSize: '18',
253 fontWeight: 'bold',
254 },
255 },
256 labelLine: {
257 show: true,
258 },
259 label: {
260 show: true,
261 minMargin: 5,
262 edgeDistance: 10,
263 lineHeight: 15,
264 formatter(data) {
265 return (
266 (
267 (data.data.value * 100) /
268 total
269 ).toFixed(2) + '%'
270 );
271 },
272 },
273 data: data.map((i) => ({
274 value: i.size,
275 name: i.name.slice(0, 7),
276 hash: i.name,
277 })),
278 },
279 ],
280 };
281
282 option && myChart.setOption(option);
283 myChart.on('click', (data) => {
284 document
285 .getElementById(data.data.hash)
286 .scrollIntoView();
287 });
288 }, 300);
289 });
290 return html`<div
291 ref=${function (dom) {
292 chartDom = dom;
293 }}
294 style="width: 600px;height:600px;margin:auto"
295 ></div>`;
296 };
297 const CharList = (data) => {
298 return data.map((i) => {
299 const chars = i.chars.startsWith('U+')
300 ? String.fromCharCode(
301 ...UnicodeRange.parse(i.chars.split(','))
302 )
303 : i.chars;
304 return html`<div>
305 <h3 id="${i.name}">
306 分片名称 ${i.name} | 分片大小 ${_formatBytes(i.size)}
307 </h3>
308 <p>${chars}</p>
309 </div>`;
310 });
311 };
312 const BaseMessage = (message) => {
313 return html`
314 <table style="margin:auto">
315 ${Object.entries(message).map((i) => {
316 return html`
317 <tr>
318 <td>${i[0].en}</td>
319 <td>${i[1].en}</td>
320 </tr>
321 `;
322 })}
323 </table>
324 `;
325 };
326 const App = () => {
327 const [data] = createResource(() =>
328 fetch('./reporter.json')
329 .then((res) => res.json())
330 .then((res) => {
331 res.message =
332 res.message.windows || res.message.macintosh;
333 return res;
334 })
335 );
336 createEffect(() => {
337 if (data()) {
338 console.log(data().message);
339 document.body.style.fontFamily = `"${
340 getFontData(data().message).fontFamily ||
341 getFontData(data().message).preferredFamily
342 }"`;
343
344 document.querySelector('title').textContent = getFontData(
345 data().message
346 ).fontFamily;
347 const link = document.createElement('link');
348 link.rel = 'stylesheet';
349 link.href =
350 './' + (data().config.cssFileName || 'result') + '.css';
351 document.head.appendChild(link);
352 }
353 });
354 const content = () =>
355 html` <div>
356 ${RangeAnalyze(
357 data().data,
358 getFontData(data().message)
359 )}
360 ${DataAnalyze(data().data, getFontData(data().message))}
361 ${TimeAnalyze(
362 data().record,
363 getFontData(data().message)
364 )}
365 ${BaseMessage(getFontData(data().message))}
366 </div>
367 ${CharList(data().data)}`;
368 return html`<div>
369 ${() => (data.loading ? `<div>加载中</div>` : content())}
370 </div>`;
371 };
372
373 import { render } from 'https://esm.sh/solid-js/web';
374 render(App, document.body);
375 </script>
376</html>