1
1
use anyhow:: Result ;
2
+ use rustc_hash:: FxHashSet ;
2
3
use serde:: Serialize ;
4
+ use tracing:: { Level , instrument} ;
3
5
use turbo_rcstr:: RcStr ;
4
- use turbo_tasks:: { FxIndexMap , FxIndexSet , ResolvedVc , Vc } ;
6
+ use turbo_tasks:: {
7
+ FxIndexMap , FxIndexSet , ResolvedVc , TryJoinIterExt , ValueToString , Vc , fxindexmap,
8
+ } ;
5
9
use turbopack_browser:: ecmascript:: EcmascriptBrowserChunk ;
6
10
use turbopack_core:: {
7
- chunk:: { Chunk , ChunkItem } ,
11
+ chunk:: { Chunk , ChunkItem , ChunkItemExt , ModuleId } ,
12
+ module:: Module ,
13
+ module_graph:: ModuleGraph ,
8
14
output:: OutputAsset ,
9
15
} ;
10
16
11
- pub async fn generate_webpack_stats < ' a , I > (
17
+ #[ instrument( level = Level :: INFO , skip_all) ]
18
+ pub async fn generate_webpack_stats < I > (
19
+ module_graph : Vc < ModuleGraph > ,
12
20
entry_name : RcStr ,
13
21
entry_assets : I ,
14
22
) -> Result < WebpackStats >
15
23
where
16
- I : IntoIterator < Item = & ' a ResolvedVc < Box < dyn OutputAsset > > > ,
24
+ I : IntoIterator < Item = ResolvedVc < Box < dyn OutputAsset > > > ,
17
25
{
18
26
let mut assets = vec ! [ ] ;
19
27
let mut chunks = vec ! [ ] ;
20
28
let mut chunk_items: FxIndexMap < Vc < Box < dyn ChunkItem > > , FxIndexSet < RcStr > > =
21
29
FxIndexMap :: default ( ) ;
22
- let mut modules = vec ! [ ] ;
30
+
31
+ let entry_assets = entry_assets. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
32
+
33
+ let ( asset_parents, asset_children) = {
34
+ let mut asset_children =
35
+ FxIndexMap :: with_capacity_and_hasher ( entry_assets. len ( ) , Default :: default ( ) ) ;
36
+ let mut visited =
37
+ FxHashSet :: with_capacity_and_hasher ( entry_assets. len ( ) , Default :: default ( ) ) ;
38
+ let mut queue = entry_assets. clone ( ) ;
39
+ while let Some ( asset) = queue. pop ( ) {
40
+ if visited. insert ( asset) {
41
+ let references = asset. references ( ) . await ?;
42
+ asset_children. insert ( asset, references. clone ( ) ) ;
43
+ queue. extend ( references) ;
44
+ }
45
+ }
46
+
47
+ let mut asset_parents: FxIndexMap < _ , Vec < _ > > =
48
+ FxIndexMap :: with_capacity_and_hasher ( entry_assets. len ( ) , Default :: default ( ) ) ;
49
+ for ( & parent, children) in & asset_children {
50
+ for child in children {
51
+ asset_parents. entry ( * child) . or_default ( ) . push ( parent) ;
52
+ }
53
+ }
54
+
55
+ ( asset_parents, asset_children)
56
+ } ;
57
+
58
+ let asset_reasons = {
59
+ let module_graph = module_graph. await ?;
60
+ let mut edges = vec ! [ ] ;
61
+ module_graph
62
+ . traverse_all_edges_unordered ( |( parent_node, r) , current| {
63
+ edges. push ( (
64
+ parent_node. module ,
65
+ RcStr :: from ( format ! ( "{}: {}" , r. chunking_type, r. export) ) ,
66
+ current. module ,
67
+ ) ) ;
68
+ Ok ( ( ) )
69
+ } )
70
+ . await ?;
71
+
72
+ let edges = edges
73
+ . into_iter ( )
74
+ . map ( async |( parent, ty, child) | {
75
+ let parent_path = parent. ident ( ) . path ( ) . await ?. path . clone ( ) ;
76
+ Ok ( (
77
+ child,
78
+ WebpackStatsReason {
79
+ module : parent_path. clone ( ) ,
80
+ module_identifier : parent. ident ( ) . to_string ( ) . owned ( ) . await ?,
81
+ module_name : parent_path,
82
+ ty,
83
+ } ,
84
+ ) )
85
+ } )
86
+ . try_join ( )
87
+ . await ?;
88
+
89
+ let mut asset_reasons: FxIndexMap < _ , Vec < _ > > = FxIndexMap :: default ( ) ;
90
+ for ( child, reason) in edges {
91
+ asset_reasons. entry ( child) . or_default ( ) . push ( reason) ;
92
+ }
93
+ asset_reasons
94
+ } ;
95
+
23
96
for asset in entry_assets {
24
- let path = normalize_client_path ( & asset. path ( ) . await ?. path ) ;
97
+ let path = RcStr :: from ( normalize_client_path ( & asset. path ( ) . await ?. path ) ) ;
25
98
26
99
let Some ( asset_len) = * asset. size_bytes ( ) . await ? else {
27
100
continue ;
28
101
} ;
29
102
30
- if let Some ( chunk) = ResolvedVc :: try_downcast_type :: < EcmascriptBrowserChunk > ( * asset) {
31
- let chunk_ident = normalize_client_path ( & chunk. path ( ) . await ?. path ) ;
103
+ if let Some ( chunk) = ResolvedVc :: try_downcast_type :: < EcmascriptBrowserChunk > ( asset) {
32
104
chunks. push ( WebpackStatsChunk {
33
105
size : asset_len,
34
- files : vec ! [ chunk_ident. clone( ) . into( ) ] ,
35
- id : chunk_ident. clone ( ) . into ( ) ,
106
+ files : vec ! [ path. clone( ) ] ,
107
+ id : path. clone ( ) ,
108
+ parents : if let Some ( parents) = asset_parents. get ( & asset) {
109
+ parents
110
+ . iter ( )
111
+ . map ( async |c| Ok ( normalize_client_path ( & c. path ( ) . await ?. path ) . into ( ) ) )
112
+ . try_join ( )
113
+ . await ?
114
+ } else {
115
+ vec ! [ ]
116
+ } ,
117
+ children : if let Some ( children) = asset_children. get ( & asset) {
118
+ children
119
+ . iter ( )
120
+ . map ( async |c| Ok ( normalize_client_path ( & c. path ( ) . await ?. path ) . into ( ) ) )
121
+ . try_join ( )
122
+ . await ?
123
+ } else {
124
+ vec ! [ ]
125
+ } ,
36
126
..Default :: default ( )
37
127
} ) ;
38
128
39
129
for item in chunk. chunk ( ) . chunk_items ( ) . await ? {
40
- // let name =
41
- chunk_items
42
- . entry ( * * item)
43
- . or_default ( )
44
- . insert ( chunk_ident. clone ( ) . into ( ) ) ;
130
+ chunk_items. entry ( * * item) . or_default ( ) . insert ( path. clone ( ) ) ;
45
131
}
46
132
}
47
133
48
134
assets. push ( WebpackStatsAsset {
49
135
ty : "asset" . into ( ) ,
50
- name : path. clone ( ) . into ( ) ,
51
- chunks : vec ! [ path. into ( ) ] ,
136
+ name : path. clone ( ) ,
137
+ chunk_names : vec ! [ path] ,
52
138
size : asset_len,
53
139
..Default :: default ( )
54
140
} ) ;
55
141
}
56
142
57
- for ( chunk_item, chunks) in chunk_items {
58
- let size = * chunk_item
59
- . content_ident ( )
60
- . path ( )
61
- . await ?
62
- . read ( )
63
- . len ( )
64
- . await ?;
65
- let path = chunk_item. asset_ident ( ) . path ( ) . await ?. path . clone ( ) ;
66
- modules. push ( WebpackStatsModule {
67
- name : path. clone ( ) ,
68
- id : path. clone ( ) ,
69
- chunks : chunks. into_iter ( ) . collect ( ) ,
70
- size,
71
- } ) ;
72
- }
143
+ // TODO try to downcast modules to `EcmascriptMergedModule` to include the scope hoisted modules
144
+ // as well
145
+
146
+ let modules = chunk_items
147
+ . into_iter ( )
148
+ . map ( async |( chunk_item, chunks) | {
149
+ let size = * chunk_item
150
+ . content_ident ( )
151
+ . path ( )
152
+ . await ?
153
+ . read ( )
154
+ . len ( )
155
+ . await ?;
156
+ Ok ( WebpackStatsModule {
157
+ name : chunk_item. asset_ident ( ) . path ( ) . await ?. path . clone ( ) ,
158
+ id : chunk_item. id ( ) . owned ( ) . await ?,
159
+ identifier : chunk_item. asset_ident ( ) . to_string ( ) . owned ( ) . await ?,
160
+ chunks : chunks. into_iter ( ) . collect ( ) ,
161
+ size,
162
+ // TODO Find all incoming edges to this module
163
+ reasons : asset_reasons
164
+ . get ( & chunk_item. module ( ) . to_resolved ( ) . await ?)
165
+ . cloned ( )
166
+ . unwrap_or_default ( ) ,
167
+ } )
168
+ } )
169
+ . try_join ( )
170
+ . await ?;
73
171
74
- let mut entrypoints = FxIndexMap :: default ( ) ;
75
- entrypoints. insert (
76
- entry_name. clone ( ) ,
172
+ let entrypoints: FxIndexMap < _ , _ > = fxindexmap ! (
173
+ entry_name. clone( ) =>
77
174
WebpackStatsEntrypoint {
78
175
name: entry_name. clone( ) ,
79
176
chunks: chunks. iter( ) . map( |c| c. id. clone( ) ) . collect( ) ,
83
180
name: a. name. clone( ) ,
84
181
} )
85
182
. collect( ) ,
86
- } ,
183
+ }
87
184
) ;
88
185
89
186
Ok ( WebpackStats {
@@ -108,35 +205,80 @@ pub struct WebpackStatsAssetInfo {}
108
205
pub struct WebpackStatsAsset {
109
206
#[ serde( rename = "type" ) ]
110
207
pub ty : RcStr ,
208
+ /// The `output` filename
111
209
pub name : RcStr ,
112
210
pub info : WebpackStatsAssetInfo ,
211
+ /// The size of the file in bytes
113
212
pub size : u64 ,
213
+ /// Indicates whether or not the asset made it to the `output` directory
114
214
pub emitted : bool ,
215
+ /// Indicates whether or not the asset was compared with the same file on the output file
216
+ /// system
115
217
pub compared_for_emit : bool ,
116
218
pub cached : bool ,
219
+ /// The chunks this asset contains
220
+ pub chunk_names : Vec < RcStr > ,
221
+ /// The chunk IDs this asset contains
117
222
pub chunks : Vec < RcStr > ,
118
223
}
119
224
120
225
#[ derive( Serialize , Debug , Default ) ]
121
226
#[ serde( rename_all = "camelCase" ) ]
122
227
pub struct WebpackStatsChunk {
228
+ /// Indicates whether or not the chunk went through Code Generation
123
229
pub rendered : bool ,
230
+ /// Indicates whether this chunk is loaded on initial page load or lazily.
124
231
pub initial : bool ,
232
+ /// Indicates whether or not the chunk contains the webpack runtime
125
233
pub entry : bool ,
126
234
pub recorded : bool ,
235
+ /// The ID of this chunk
127
236
pub id : RcStr ,
237
+ /// Chunk size in bytes
128
238
pub size : u64 ,
129
239
pub hash : RcStr ,
240
+ /// An array of filename strings that contain this chunk
130
241
pub files : Vec < RcStr > ,
242
+ /// An list of chunk names contained within this chunk
243
+ pub names : Vec < RcStr > ,
244
+ /// Parent chunk IDs
245
+ pub parents : Vec < RcStr > ,
246
+ /// Child chunk IDs
247
+ pub children : Vec < RcStr > ,
131
248
}
132
249
133
250
#[ derive( Serialize , Debug ) ]
134
251
#[ serde( rename_all = "camelCase" ) ]
135
252
pub struct WebpackStatsModule {
253
+ /// Path to the actual file
136
254
pub name : RcStr ,
137
- pub id : RcStr ,
255
+ /// The ID of the module
256
+ pub id : ModuleId ,
257
+ /// A unique ID used internally
258
+ pub identifier : RcStr ,
138
259
pub chunks : Vec < RcStr > ,
139
260
pub size : Option < u64 > ,
261
+ pub reasons : Vec < WebpackStatsReason > ,
262
+ }
263
+
264
+ #[ derive( Clone , Serialize , Debug ) ]
265
+ #[ serde( rename_all = "camelCase" ) ]
266
+ pub struct WebpackStatsReason {
267
+ /// The [WebpackStatsModule::name]
268
+ pub module : RcStr ,
269
+ // /// The [WebpackStatsModule::id]
270
+ // pub module_id: ModuleId,
271
+ /// The [WebpackStatsModule::identifier]
272
+ pub module_identifier : RcStr ,
273
+ /// A more readable name for the module (used for "pretty-printing")
274
+ pub module_name : RcStr ,
275
+ /// The [type of request](/api/module-methods) used
276
+ #[ serde( rename = "type" ) ]
277
+ pub ty : RcStr ,
278
+ // /// Raw string used for the `import` or `require` request
279
+ // pub user_request: RcStr,
280
+ // /// Lines of code that caused the module to be included
281
+ // pub loc: RcStr
140
282
}
141
283
142
284
#[ derive( Serialize , Debug ) ]
0 commit comments