@playcraft/build 0.0.8 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -6
- package/dist/analyzers/build-analyzer.d.ts +98 -0
- package/dist/analyzers/build-analyzer.js +1160 -0
- package/dist/analyzers/enhanced-report-template.d.ts +13 -0
- package/dist/analyzers/enhanced-report-template.js +957 -0
- package/dist/analyzers/index.d.ts +6 -0
- package/dist/analyzers/index.js +9 -0
- package/dist/analyzers/optimization-analyzer.d.ts +88 -0
- package/dist/analyzers/optimization-analyzer.js +278 -0
- package/dist/analyzers/playable-analyzer.d.ts +91 -0
- package/dist/analyzers/playable-analyzer.js +976 -0
- package/dist/analyzers/report-template.d.ts +50 -0
- package/dist/analyzers/report-template.js +591 -0
- package/dist/analyzers/scene-asset-collector.js +8 -0
- package/dist/base-builder.d.ts +9 -0
- package/dist/base-builder.js +149 -1
- package/dist/build-state-manager.d.ts +110 -0
- package/dist/build-state-manager.js +169 -0
- package/dist/generators/config-generator.d.ts +2 -0
- package/dist/generators/config-generator.js +179 -10
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/loaders/playcanvas-loader.d.ts +7 -0
- package/dist/loaders/playcanvas-loader.js +17 -0
- package/dist/state/build-state-manager.d.ts +174 -0
- package/dist/state/build-state-manager.js +235 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +2 -0
- package/dist/state/state-to-report-converter.d.ts +141 -0
- package/dist/state/state-to-report-converter.js +177 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +11 -0
- package/dist/vite/config-builder.js +8 -1
- package/dist/vite/plugin-build-state.d.ts +11 -0
- package/dist/vite/plugin-build-state.js +145 -0
- package/dist/vite/plugin-source-builder.js +1 -0
- package/package.json +12 -12
- package/dist/templates/__loading__.js +0 -100
- package/dist/templates/__modules__.js +0 -47
- package/dist/templates/__settings__.template.js +0 -20
- package/dist/templates/__start__.js +0 -332
- package/dist/templates/index.html +0 -18
- package/dist/templates/logo.png +0 -0
- package/dist/templates/manifest.json +0 -1
- package/dist/templates/patches/cannon.min.js +0 -28
- package/dist/templates/patches/lz4.js +0 -10
- package/dist/templates/patches/one-page-http-get.js +0 -20
- package/dist/templates/patches/one-page-inline-game-scripts.js +0 -52
- package/dist/templates/patches/one-page-mraid-resize-canvas.js +0 -46
- package/dist/templates/patches/p2.min.js +0 -27
- package/dist/templates/patches/playcraft-no-xhr.js +0 -76
- package/dist/templates/playcanvas-stable.min.js +0 -16363
- package/dist/templates/styles.css +0 -43
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一的分析报告数据接口
|
|
3
|
+
*/
|
|
4
|
+
export interface UnifiedReportData {
|
|
5
|
+
reportType: 'base-build' | 'playable';
|
|
6
|
+
title: string;
|
|
7
|
+
buildPath: string;
|
|
8
|
+
totalSize: number;
|
|
9
|
+
totalSizeFormatted: string;
|
|
10
|
+
items: Array<{
|
|
11
|
+
name: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
type: string;
|
|
14
|
+
category: string;
|
|
15
|
+
size: number;
|
|
16
|
+
sizeFormatted: string;
|
|
17
|
+
percentage: number;
|
|
18
|
+
dataUrlSize?: number;
|
|
19
|
+
dataUrlSizeFormatted?: string;
|
|
20
|
+
}>;
|
|
21
|
+
byCategory: Record<string, {
|
|
22
|
+
count: number;
|
|
23
|
+
size: number;
|
|
24
|
+
sizeFormatted: string;
|
|
25
|
+
percentage: number;
|
|
26
|
+
dataUrlSize?: number;
|
|
27
|
+
dataUrlSizeFormatted?: string;
|
|
28
|
+
}>;
|
|
29
|
+
byType: Record<string, {
|
|
30
|
+
count: number;
|
|
31
|
+
size: number;
|
|
32
|
+
sizeFormatted: string;
|
|
33
|
+
percentage: number;
|
|
34
|
+
dataUrlSize?: number;
|
|
35
|
+
dataUrlSizeFormatted?: string;
|
|
36
|
+
}>;
|
|
37
|
+
optimizationInfo?: {
|
|
38
|
+
baseBuildSize?: number;
|
|
39
|
+
baseBuildSizeFormatted?: string;
|
|
40
|
+
actualSize?: number;
|
|
41
|
+
actualSizeFormatted?: string;
|
|
42
|
+
optimizationRate?: number;
|
|
43
|
+
};
|
|
44
|
+
estimatedHtmlSize?: number;
|
|
45
|
+
estimatedHtmlSizeFormatted?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 生成统一的 HTML 报告模板
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateUnifiedReportHTML(data: UnifiedReportData): string;
|
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生成统一的 HTML 报告模板
|
|
3
|
+
*/
|
|
4
|
+
export function generateUnifiedReportHTML(data) {
|
|
5
|
+
const isBaseBuild = data.reportType === 'base-build';
|
|
6
|
+
const isPlayable = data.reportType === 'playable';
|
|
7
|
+
// 生成分类统计表格
|
|
8
|
+
const categoryRows = Object.entries(data.byCategory)
|
|
9
|
+
.sort((a, b) => b[1].size - a[1].size)
|
|
10
|
+
.map(([category, stats]) => {
|
|
11
|
+
const dataUrlColumn = isBaseBuild && stats.dataUrlSize
|
|
12
|
+
? `<td>${stats.dataUrlSizeFormatted}</td>`
|
|
13
|
+
: '';
|
|
14
|
+
return `
|
|
15
|
+
<tr>
|
|
16
|
+
<td>${category}</td>
|
|
17
|
+
<td>${stats.count}</td>
|
|
18
|
+
<td>${stats.sizeFormatted}</td>
|
|
19
|
+
${dataUrlColumn}
|
|
20
|
+
<td>${stats.percentage.toFixed(2)}%</td>
|
|
21
|
+
<td>
|
|
22
|
+
<div class="bar" style="width: ${stats.percentage.toFixed(1)}%"></div>
|
|
23
|
+
</td>
|
|
24
|
+
</tr>
|
|
25
|
+
`;
|
|
26
|
+
}).join('');
|
|
27
|
+
// 生成类型统计表格
|
|
28
|
+
const typeRows = Object.entries(data.byType)
|
|
29
|
+
.sort((a, b) => b[1].size - a[1].size)
|
|
30
|
+
.map(([type, stats]) => {
|
|
31
|
+
const dataUrlColumn = isBaseBuild && stats.dataUrlSize
|
|
32
|
+
? `<td>${stats.dataUrlSizeFormatted}</td>`
|
|
33
|
+
: '';
|
|
34
|
+
return `
|
|
35
|
+
<tr>
|
|
36
|
+
<td>${type}</td>
|
|
37
|
+
<td>${stats.count}</td>
|
|
38
|
+
<td>${stats.sizeFormatted}</td>
|
|
39
|
+
${dataUrlColumn}
|
|
40
|
+
<td>${stats.percentage.toFixed(2)}%</td>
|
|
41
|
+
<td>
|
|
42
|
+
<div class="bar" style="width: ${stats.percentage.toFixed(1)}%"></div>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
`;
|
|
46
|
+
}).join('');
|
|
47
|
+
// 生成 Top 资源表格
|
|
48
|
+
const topItems = data.items.slice(0, 20);
|
|
49
|
+
const itemRows = topItems.map(item => {
|
|
50
|
+
const dataUrlColumn = isBaseBuild && item.dataUrlSize
|
|
51
|
+
? `<td>${item.dataUrlSizeFormatted}</td>`
|
|
52
|
+
: '';
|
|
53
|
+
return `
|
|
54
|
+
<tr>
|
|
55
|
+
<td>${item.name}</td>
|
|
56
|
+
<td>${item.type}</td>
|
|
57
|
+
<td>${item.category}</td>
|
|
58
|
+
<td>${item.sizeFormatted}</td>
|
|
59
|
+
${dataUrlColumn}
|
|
60
|
+
<td>${item.percentage.toFixed(2)}%</td>
|
|
61
|
+
<td>
|
|
62
|
+
<div class="bar" style="width: ${item.percentage.toFixed(1)}%"></div>
|
|
63
|
+
</td>
|
|
64
|
+
</tr>
|
|
65
|
+
`;
|
|
66
|
+
}).join('');
|
|
67
|
+
// 表头
|
|
68
|
+
const dataUrlHeader = isBaseBuild ? '<th>Data URL 大小</th>' : '';
|
|
69
|
+
// 优化信息部分
|
|
70
|
+
const optimizationSection = data.optimizationInfo ? `
|
|
71
|
+
<div class="optimization-info">
|
|
72
|
+
<h2>🎯 优化效果</h2>
|
|
73
|
+
<div class="optimization-stats">
|
|
74
|
+
<div class="optimization-item">
|
|
75
|
+
<div class="optimization-label">Base Build 预估大小</div>
|
|
76
|
+
<div class="optimization-value">${data.optimizationInfo.baseBuildSizeFormatted}</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="optimization-item">
|
|
79
|
+
<div class="optimization-label">实际打包大小</div>
|
|
80
|
+
<div class="optimization-value">${data.optimizationInfo.actualSizeFormatted}</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="optimization-item">
|
|
83
|
+
<div class="optimization-label">优化率</div>
|
|
84
|
+
<div class="optimization-value optimization-rate">${data.optimizationInfo.optimizationRate?.toFixed(1)}%</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
` : '';
|
|
89
|
+
// Base Build 特有的预估信息
|
|
90
|
+
const estimatedSection = isBaseBuild && data.estimatedHtmlSize ? `
|
|
91
|
+
<div class="stat-item">
|
|
92
|
+
<div class="stat-label">预估 HTML 大小</div>
|
|
93
|
+
<div class="stat-value">${data.estimatedHtmlSizeFormatted}</div>
|
|
94
|
+
</div>
|
|
95
|
+
` : '';
|
|
96
|
+
return `<!DOCTYPE html>
|
|
97
|
+
<html lang="zh-CN">
|
|
98
|
+
<head>
|
|
99
|
+
<meta charset="UTF-8">
|
|
100
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
101
|
+
<title>${data.title}</title>
|
|
102
|
+
<style>
|
|
103
|
+
* {
|
|
104
|
+
margin: 0;
|
|
105
|
+
padding: 0;
|
|
106
|
+
box-sizing: border-box;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
body {
|
|
110
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
111
|
+
background: #1e1e1e;
|
|
112
|
+
color: #e0e0e0;
|
|
113
|
+
padding: 20px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.container {
|
|
117
|
+
max-width: 1400px;
|
|
118
|
+
margin: 0 auto;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.header {
|
|
122
|
+
background: #252526;
|
|
123
|
+
padding: 30px;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
margin-bottom: 20px;
|
|
126
|
+
border: 1px solid #3e3e42;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.header h1 {
|
|
130
|
+
font-size: 24px;
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
color: #e0e0e0;
|
|
133
|
+
margin-bottom: 20px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.header-stats {
|
|
137
|
+
display: grid;
|
|
138
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
139
|
+
gap: 20px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.stat-item {
|
|
143
|
+
background: #1e1e1e;
|
|
144
|
+
padding: 15px;
|
|
145
|
+
border-radius: 6px;
|
|
146
|
+
border: 1px solid #3e3e42;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.stat-label {
|
|
150
|
+
color: #888;
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
text-transform: uppercase;
|
|
153
|
+
letter-spacing: 0.5px;
|
|
154
|
+
margin-bottom: 8px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.stat-value {
|
|
158
|
+
color: #4ec9b0;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
font-size: 20px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.optimization-info {
|
|
164
|
+
background: #252526;
|
|
165
|
+
padding: 30px;
|
|
166
|
+
border-radius: 8px;
|
|
167
|
+
margin-bottom: 20px;
|
|
168
|
+
border: 1px solid #3e3e42;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.optimization-info h2 {
|
|
172
|
+
font-size: 18px;
|
|
173
|
+
margin-bottom: 20px;
|
|
174
|
+
color: #e0e0e0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.optimization-stats {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
180
|
+
gap: 20px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.optimization-item {
|
|
184
|
+
background: #1e1e1e;
|
|
185
|
+
padding: 20px;
|
|
186
|
+
border-radius: 6px;
|
|
187
|
+
border: 1px solid #3e3e42;
|
|
188
|
+
text-align: center;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.optimization-label {
|
|
192
|
+
color: #888;
|
|
193
|
+
font-size: 13px;
|
|
194
|
+
margin-bottom: 10px;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.optimization-value {
|
|
198
|
+
color: #4ec9b0;
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
font-size: 24px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.optimization-rate {
|
|
204
|
+
color: #2ecc71;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.section {
|
|
208
|
+
background: #252526;
|
|
209
|
+
padding: 30px;
|
|
210
|
+
border-radius: 8px;
|
|
211
|
+
margin-bottom: 20px;
|
|
212
|
+
border: 1px solid #3e3e42;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.section h2 {
|
|
216
|
+
font-size: 18px;
|
|
217
|
+
margin-bottom: 20px;
|
|
218
|
+
color: #e0e0e0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
table {
|
|
222
|
+
width: 100%;
|
|
223
|
+
border-collapse: collapse;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
th, td {
|
|
227
|
+
padding: 12px;
|
|
228
|
+
text-align: left;
|
|
229
|
+
border-bottom: 1px solid #3e3e42;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
th {
|
|
233
|
+
background: #1e1e1e;
|
|
234
|
+
color: #888;
|
|
235
|
+
font-size: 12px;
|
|
236
|
+
text-transform: uppercase;
|
|
237
|
+
letter-spacing: 0.5px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
td {
|
|
242
|
+
color: #e0e0e0;
|
|
243
|
+
font-size: 13px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
tr:hover {
|
|
247
|
+
background: #2a2a2a;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.bar {
|
|
251
|
+
height: 20px;
|
|
252
|
+
background: linear-gradient(90deg, #4ec9b0, #2ecc71);
|
|
253
|
+
border-radius: 4px;
|
|
254
|
+
min-width: 2px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.treemap-container {
|
|
258
|
+
background: #252526;
|
|
259
|
+
padding: 30px;
|
|
260
|
+
border-radius: 8px;
|
|
261
|
+
margin-bottom: 20px;
|
|
262
|
+
border: 1px solid #3e3e42;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.treemap {
|
|
266
|
+
width: 100%;
|
|
267
|
+
min-height: 600px;
|
|
268
|
+
position: relative;
|
|
269
|
+
background: #1e1e1e;
|
|
270
|
+
border-radius: 6px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.treemap-block {
|
|
274
|
+
position: absolute;
|
|
275
|
+
border: 2px solid #1e1e1e;
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
transition: all 0.2s;
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
display: flex;
|
|
280
|
+
flex-direction: column;
|
|
281
|
+
justify-content: center;
|
|
282
|
+
align-items: center;
|
|
283
|
+
padding: 8px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.treemap-block:hover {
|
|
287
|
+
border-color: #fff;
|
|
288
|
+
z-index: 10;
|
|
289
|
+
transform: scale(1.02);
|
|
290
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.treemap-block-label {
|
|
294
|
+
font-size: 11px;
|
|
295
|
+
font-weight: 600;
|
|
296
|
+
color: white;
|
|
297
|
+
text-align: center;
|
|
298
|
+
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
|
|
299
|
+
word-break: break-word;
|
|
300
|
+
line-height: 1.3;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.treemap-block-size {
|
|
304
|
+
font-size: 10px;
|
|
305
|
+
color: rgba(255,255,255,0.8);
|
|
306
|
+
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
|
|
307
|
+
margin-top: 4px;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.tooltip {
|
|
311
|
+
position: fixed;
|
|
312
|
+
background: rgba(0, 0, 0, 0.95);
|
|
313
|
+
color: white;
|
|
314
|
+
padding: 12px;
|
|
315
|
+
border-radius: 6px;
|
|
316
|
+
font-size: 12px;
|
|
317
|
+
pointer-events: none;
|
|
318
|
+
z-index: 1000;
|
|
319
|
+
display: none;
|
|
320
|
+
border: 1px solid #555;
|
|
321
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
|
|
322
|
+
max-width: 300px;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.tooltip-title {
|
|
326
|
+
font-weight: 600;
|
|
327
|
+
margin-bottom: 6px;
|
|
328
|
+
color: #4ec9b0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.tooltip-row {
|
|
332
|
+
display: flex;
|
|
333
|
+
justify-content: space-between;
|
|
334
|
+
margin: 4px 0;
|
|
335
|
+
gap: 20px;
|
|
336
|
+
}
|
|
337
|
+
</style>
|
|
338
|
+
</head>
|
|
339
|
+
<body>
|
|
340
|
+
<div class="container">
|
|
341
|
+
<div class="header">
|
|
342
|
+
<h1>${data.title}</h1>
|
|
343
|
+
<div class="header-stats">
|
|
344
|
+
<div class="stat-item">
|
|
345
|
+
<div class="stat-label">${isBaseBuild ? '构建目录' : 'HTML 文件'}</div>
|
|
346
|
+
<div class="stat-value">${data.buildPath}</div>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="stat-item">
|
|
349
|
+
<div class="stat-label">${isBaseBuild ? '文件总大小' : '文件大小'}</div>
|
|
350
|
+
<div class="stat-value">${data.totalSizeFormatted}</div>
|
|
351
|
+
</div>
|
|
352
|
+
<div class="stat-item">
|
|
353
|
+
<div class="stat-label">${isBaseBuild ? '文件数量' : '资源数量'}</div>
|
|
354
|
+
<div class="stat-value">${data.items.length}</div>
|
|
355
|
+
</div>
|
|
356
|
+
${estimatedSection}
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
${optimizationSection}
|
|
361
|
+
|
|
362
|
+
<div class="treemap-container">
|
|
363
|
+
<h2>🗺️ ${isBaseBuild ? '文件' : '资源'}分布图</h2>
|
|
364
|
+
<div class="treemap" id="treemap"></div>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<div class="section">
|
|
368
|
+
<h2>📦 按分类统计</h2>
|
|
369
|
+
<table>
|
|
370
|
+
<thead>
|
|
371
|
+
<tr>
|
|
372
|
+
<th>分类</th>
|
|
373
|
+
<th>数量</th>
|
|
374
|
+
<th>大小</th>
|
|
375
|
+
${dataUrlHeader}
|
|
376
|
+
<th>占比</th>
|
|
377
|
+
<th>可视化</th>
|
|
378
|
+
</tr>
|
|
379
|
+
</thead>
|
|
380
|
+
<tbody>
|
|
381
|
+
${categoryRows}
|
|
382
|
+
</tbody>
|
|
383
|
+
</table>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<div class="section">
|
|
387
|
+
<h2>📄 按类型统计</h2>
|
|
388
|
+
<table>
|
|
389
|
+
<thead>
|
|
390
|
+
<tr>
|
|
391
|
+
<th>类型</th>
|
|
392
|
+
<th>数量</th>
|
|
393
|
+
<th>大小</th>
|
|
394
|
+
${dataUrlHeader}
|
|
395
|
+
<th>占比</th>
|
|
396
|
+
<th>可视化</th>
|
|
397
|
+
</tr>
|
|
398
|
+
</thead>
|
|
399
|
+
<tbody>
|
|
400
|
+
${typeRows}
|
|
401
|
+
</tbody>
|
|
402
|
+
</table>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<div class="section">
|
|
406
|
+
<h2>🔝 Top 20 ${isBaseBuild ? '文件' : '资源'}</h2>
|
|
407
|
+
<table>
|
|
408
|
+
<thead>
|
|
409
|
+
<tr>
|
|
410
|
+
<th>名称</th>
|
|
411
|
+
<th>类型</th>
|
|
412
|
+
<th>分类</th>
|
|
413
|
+
<th>大小</th>
|
|
414
|
+
${dataUrlHeader}
|
|
415
|
+
<th>占比</th>
|
|
416
|
+
<th>可视化</th>
|
|
417
|
+
</tr>
|
|
418
|
+
</thead>
|
|
419
|
+
<tbody>
|
|
420
|
+
${itemRows}
|
|
421
|
+
</tbody>
|
|
422
|
+
</table>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div class="tooltip" id="tooltip"></div>
|
|
427
|
+
|
|
428
|
+
<script>
|
|
429
|
+
// 嵌入数据
|
|
430
|
+
const reportData = ${JSON.stringify(data, null, 2)};
|
|
431
|
+
const isBaseBuild = reportData.reportType === 'base-build';
|
|
432
|
+
|
|
433
|
+
const categoryColors = {
|
|
434
|
+
'engine': '#e74c3c',
|
|
435
|
+
'config': '#3498db',
|
|
436
|
+
'scene': '#2ecc71',
|
|
437
|
+
'script': '#f39c12',
|
|
438
|
+
'asset': '#9b59b6',
|
|
439
|
+
'html': '#1abc9c'
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const flattenItems = () => {
|
|
443
|
+
return reportData.items.map(item => ({
|
|
444
|
+
...item,
|
|
445
|
+
value: item.size
|
|
446
|
+
}));
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const layoutTreemap = (items, x, y, width, height) => {
|
|
450
|
+
const sortedItems = [...items].sort((a, b) => b.value - a.value);
|
|
451
|
+
const total = sortedItems.reduce((sum, item) => sum + item.value, 0);
|
|
452
|
+
if (total === 0) return [];
|
|
453
|
+
|
|
454
|
+
const blocks = [];
|
|
455
|
+
let currentY = y;
|
|
456
|
+
let rowItems = [];
|
|
457
|
+
let rowSize = 0;
|
|
458
|
+
|
|
459
|
+
sortedItems.forEach((item, index) => {
|
|
460
|
+
rowItems.push(item);
|
|
461
|
+
rowSize += item.value;
|
|
462
|
+
|
|
463
|
+
const shouldBreak = rowItems.length >= 10 || index === sortedItems.length - 1;
|
|
464
|
+
|
|
465
|
+
if (shouldBreak) {
|
|
466
|
+
const rowRatio = rowSize / total;
|
|
467
|
+
const rowHeight = height * rowRatio;
|
|
468
|
+
|
|
469
|
+
let offsetX = x;
|
|
470
|
+
rowItems.forEach(i => {
|
|
471
|
+
const iWidth = (i.value / rowSize) * width;
|
|
472
|
+
|
|
473
|
+
blocks.push({
|
|
474
|
+
...i,
|
|
475
|
+
x: offsetX,
|
|
476
|
+
y: currentY,
|
|
477
|
+
width: Math.max(iWidth, 1),
|
|
478
|
+
height: Math.max(rowHeight, 1)
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
offsetX += iWidth;
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
currentY += rowHeight;
|
|
485
|
+
rowItems = [];
|
|
486
|
+
rowSize = 0;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return blocks;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const renderTreemap = () => {
|
|
494
|
+
const container = document.getElementById('treemap');
|
|
495
|
+
const width = container.clientWidth;
|
|
496
|
+
const height = 600;
|
|
497
|
+
|
|
498
|
+
container.innerHTML = '';
|
|
499
|
+
container.style.height = height + 'px';
|
|
500
|
+
|
|
501
|
+
const items = flattenItems();
|
|
502
|
+
const blocks = layoutTreemap(items, 0, 0, width, height);
|
|
503
|
+
|
|
504
|
+
blocks.forEach(block => {
|
|
505
|
+
const div = document.createElement('div');
|
|
506
|
+
div.className = 'treemap-block';
|
|
507
|
+
div.style.left = block.x + 'px';
|
|
508
|
+
div.style.top = block.y + 'px';
|
|
509
|
+
div.style.width = block.width + 'px';
|
|
510
|
+
div.style.height = block.height + 'px';
|
|
511
|
+
div.style.background = categoryColors[block.category] || '#95a5a6';
|
|
512
|
+
|
|
513
|
+
const showLabel = block.width > 60 && block.height > 40;
|
|
514
|
+
|
|
515
|
+
if (showLabel) {
|
|
516
|
+
const label = document.createElement('div');
|
|
517
|
+
label.className = 'treemap-block-label';
|
|
518
|
+
label.textContent = block.name;
|
|
519
|
+
div.appendChild(label);
|
|
520
|
+
|
|
521
|
+
const size = document.createElement('div');
|
|
522
|
+
size.className = 'treemap-block-size';
|
|
523
|
+
size.textContent = block.sizeFormatted;
|
|
524
|
+
div.appendChild(size);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
div.addEventListener('mouseenter', (e) => {
|
|
528
|
+
const tooltip = document.getElementById('tooltip');
|
|
529
|
+
let tooltipContent = \`
|
|
530
|
+
<div class="tooltip-title">\${block.name}</div>
|
|
531
|
+
<div class="tooltip-row">
|
|
532
|
+
<span>类型:</span>
|
|
533
|
+
<span>\${block.type}</span>
|
|
534
|
+
</div>
|
|
535
|
+
<div class="tooltip-row">
|
|
536
|
+
<span>分类:</span>
|
|
537
|
+
<span>\${block.category}</span>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="tooltip-row">
|
|
540
|
+
<span>大小:</span>
|
|
541
|
+
<span>\${block.sizeFormatted}</span>
|
|
542
|
+
</div>
|
|
543
|
+
\`;
|
|
544
|
+
|
|
545
|
+
if (isBaseBuild && block.dataUrlSize) {
|
|
546
|
+
tooltipContent += \`
|
|
547
|
+
<div class="tooltip-row">
|
|
548
|
+
<span>Data URL:</span>
|
|
549
|
+
<span>\${block.dataUrlSizeFormatted}</span>
|
|
550
|
+
</div>
|
|
551
|
+
\`;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
tooltipContent += \`
|
|
555
|
+
<div class="tooltip-row">
|
|
556
|
+
<span>占比:</span>
|
|
557
|
+
<span>\${block.percentage.toFixed(2)}%</span>
|
|
558
|
+
</div>
|
|
559
|
+
\`;
|
|
560
|
+
|
|
561
|
+
tooltip.innerHTML = tooltipContent;
|
|
562
|
+
tooltip.style.display = 'block';
|
|
563
|
+
tooltip.style.left = (e.clientX + 15) + 'px';
|
|
564
|
+
tooltip.style.top = (e.clientY + 15) + 'px';
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
div.addEventListener('mousemove', (e) => {
|
|
568
|
+
const tooltip = document.getElementById('tooltip');
|
|
569
|
+
tooltip.style.left = (e.clientX + 15) + 'px';
|
|
570
|
+
tooltip.style.top = (e.clientY + 15) + 'px';
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
div.addEventListener('mouseleave', () => {
|
|
574
|
+
document.getElementById('tooltip').style.display = 'none';
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
container.appendChild(div);
|
|
578
|
+
});
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
renderTreemap();
|
|
582
|
+
|
|
583
|
+
let resizeTimeout;
|
|
584
|
+
window.addEventListener('resize', () => {
|
|
585
|
+
clearTimeout(resizeTimeout);
|
|
586
|
+
resizeTimeout = setTimeout(renderTreemap, 300);
|
|
587
|
+
});
|
|
588
|
+
</script>
|
|
589
|
+
</body>
|
|
590
|
+
</html>`;
|
|
591
|
+
}
|
|
@@ -112,6 +112,14 @@ function collectSettingsAssets(settings, deps, assets) {
|
|
|
112
112
|
function collectEntityAssets(entity, deps, assets) {
|
|
113
113
|
if (!entity || typeof entity !== 'object')
|
|
114
114
|
return;
|
|
115
|
+
// 检查实体是否引用了 template(template_id 字段)
|
|
116
|
+
if (entity.template_id) {
|
|
117
|
+
const templateId = String(entity.template_id);
|
|
118
|
+
if (assets[templateId]) {
|
|
119
|
+
deps.directAssets.add(templateId);
|
|
120
|
+
console.log(` [Template] 实体 "${entity.name || 'unnamed'}" 引用 template: ${templateId}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
115
123
|
// 扫描所有组件
|
|
116
124
|
if (entity.components) {
|
|
117
125
|
for (const [componentName, componentData] of Object.entries(entity.components)) {
|
package/dist/base-builder.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface BaseBuildOutput {
|
|
|
32
32
|
export declare class BaseBuilder {
|
|
33
33
|
private projectDir;
|
|
34
34
|
private options;
|
|
35
|
+
private stateManager;
|
|
35
36
|
constructor(projectDir: string, options: BaseBuildOptions);
|
|
36
37
|
/**
|
|
37
38
|
* 执行基础构建
|
|
@@ -81,4 +82,12 @@ export declare class BaseBuilder {
|
|
|
81
82
|
* 递归扫描目录
|
|
82
83
|
*/
|
|
83
84
|
private scanDirectory;
|
|
85
|
+
/**
|
|
86
|
+
* 记录构建产物信息到状态管理器
|
|
87
|
+
*/
|
|
88
|
+
private recordBuildOutput;
|
|
89
|
+
/**
|
|
90
|
+
* 生成构建分析报告
|
|
91
|
+
*/
|
|
92
|
+
private generateAnalysisReport;
|
|
84
93
|
}
|