@@ -20,7 +20,10 @@ use swc_common::{
2020} ;
2121use swc_config:: { file_pattern:: FilePattern , is_module:: IsModule , types:: BoolOr } ;
2222use swc_ecma_ast:: { EsVersion , Ident , IdentName , Program } ;
23- use swc_ecma_codegen:: { text_writer:: WriteJs , Emitter , Node } ;
23+ use swc_ecma_codegen:: {
24+ text_writer:: { ScopeRecord , WriteJs } ,
25+ Emitter , Node ,
26+ } ;
2427use swc_ecma_minifier:: js:: JsMinifyCommentOption ;
2528use swc_ecma_parser:: {
2629 parse_file_as_commonjs, parse_file_as_module, parse_file_as_program, parse_file_as_script,
@@ -29,6 +32,8 @@ use swc_ecma_parser::{
2932use swc_ecma_visit:: { noop_visit_type, Visit , VisitWith } ;
3033use swc_timer:: timer;
3134
35+ mod source_map_scopes;
36+
3237#[ cfg( feature = "node" ) ]
3338#[ napi_derive:: napi( object) ]
3439#[ derive( Debug , Serialize ) ]
@@ -123,6 +128,7 @@ pub struct PrintArgs<'a> {
123128 pub orig : Option < swc_sourcemap:: SourceMap > ,
124129 pub comments : Option < & ' a dyn Comments > ,
125130 pub emit_source_map_columns : bool ,
131+ pub emit_source_map_scopes : bool ,
126132 pub preamble : & ' a str ,
127133 pub codegen_config : swc_ecma_codegen:: Config ,
128134 pub output : Option < FxHashMap < String , String > > ,
@@ -144,6 +150,7 @@ impl Default for PrintArgs<'_> {
144150 orig : None ,
145151 comments : None ,
146152 emit_source_map_columns : false ,
153+ emit_source_map_scopes : false ,
147154 preamble : "" ,
148155 codegen_config : Default :: default ( ) ,
149156 output : None ,
@@ -176,6 +183,7 @@ pub fn print<T>(
176183 orig,
177184 comments,
178185 emit_source_map_columns,
186+ emit_source_map_scopes,
179187 preamble,
180188 codegen_config,
181189 output,
@@ -189,11 +197,13 @@ where
189197 let _timer = timer ! ( "Compiler::print" ) ;
190198
191199 let mut src_map_buf = Vec :: new ( ) ;
200+ let should_emit_scope_map = source_map. enabled ( ) && emit_source_map_scopes && orig. is_none ( ) ;
201+ let mut scope_buf = should_emit_scope_map. then ( Vec :: < ScopeRecord > :: new) ;
192202
193203 let mut src = {
194204 let mut buf = std:: vec:: Vec :: new ( ) ;
195205 {
196- let mut w = swc_ecma_codegen:: text_writer:: JsWriter :: new (
206+ let mut w = swc_ecma_codegen:: text_writer:: JsWriter :: new_with_scopes (
197207 cm. clone ( ) ,
198208 "\n " ,
199209 & mut buf,
@@ -202,6 +212,7 @@ where
202212 } else {
203213 None
204214 } ,
215+ scope_buf. as_mut ( ) ,
205216 ) ;
206217 w. preamble ( preamble) . unwrap ( ) ;
207218 let mut wr = Box :: new ( w) as Box < dyn WriteJs > ;
@@ -233,6 +244,11 @@ where
233244 panic ! ( "The module contains only dummy spans\n {src}" ) ;
234245 }
235246
247+ let additional_scope_names = scope_buf
248+ . as_deref ( )
249+ . map ( source_map_scopes:: collect_additional_names)
250+ . unwrap_or_default ( ) ;
251+
236252 let mut map = if source_map. enabled ( ) {
237253 Some ( cm. build_source_map (
238254 & src_map_buf,
@@ -241,6 +257,7 @@ where
241257 source_file_name,
242258 output_path : output_path. as_deref ( ) ,
243259 names : source_map_names,
260+ additional_names : & additional_scope_names,
244261 inline_sources_content,
245262 emit_columns : emit_source_map_columns,
246263 ignore_list : source_map_ignore_list,
@@ -251,6 +268,19 @@ where
251268 } ;
252269
253270 if let Some ( map) = & mut map {
271+ if should_emit_scope_map {
272+ if let Some ( scope_buf) = scope_buf. as_deref ( ) {
273+ let encoded_scopes =
274+ source_map_scopes:: encode_scopes ( scope_buf, & cm, map, |file_name| {
275+ map_file_name_to_source ( source_file_name, output_path. as_deref ( ) , file_name)
276+ } ) ;
277+
278+ if let Some ( scopes) = encoded_scopes {
279+ map. set_scopes ( Some ( scopes) ) ;
280+ }
281+ }
282+ }
283+
254284 if let Some ( source_root) = source_root {
255285 map. set_source_root ( Some ( BytesStr :: from_str_slice ( source_root) ) )
256286 }
@@ -306,6 +336,7 @@ struct SwcSourceMapConfig<'a> {
306336 output_path : Option < & ' a Path > ,
307337
308338 names : & ' a FxHashMap < BytePos , Atom > ,
339+ additional_names : & ' a [ String ] ,
309340
310341 inline_sources_content : bool ,
311342
@@ -314,38 +345,52 @@ struct SwcSourceMapConfig<'a> {
314345 ignore_list : Option < FilePattern > ,
315346}
316347
317- impl SourceMapGenConfig for SwcSourceMapConfig < ' _ > {
318- fn file_name_to_source ( & self , f : & FileName ) -> String {
319- if let Some ( file_name) = self . source_file_name {
320- return file_name. to_string ( ) ;
321- }
348+ fn map_file_name_to_source (
349+ source_file_name : Option < & str > ,
350+ output_path : Option < & Path > ,
351+ f : & FileName ,
352+ ) -> String {
353+ if let Some ( file_name) = source_file_name {
354+ return file_name. to_string ( ) ;
355+ }
322356
323- let Some ( base_path) = self . output_path . as_ref ( ) . and_then ( |v| v. parent ( ) ) else {
324- return f. to_string ( ) ;
325- } ;
326- let target = match f {
327- FileName :: Real ( v) => v,
328- _ => return f. to_string ( ) ,
329- } ;
357+ let Some ( base_path) = output_path. and_then ( |v| v. parent ( ) ) else {
358+ return f. to_string ( ) ;
359+ } ;
360+ let target = match f {
361+ FileName :: Real ( v) => v,
362+ _ => return f. to_string ( ) ,
363+ } ;
330364
331- let rel = pathdiff:: diff_paths ( target, base_path) ;
332- match rel {
333- Some ( v) => {
334- let s = v. to_string_lossy ( ) . to_string ( ) ;
335- if cfg ! ( target_os = "windows" ) {
336- s. replace ( '\\' , "/" )
337- } else {
338- s
339- }
365+ let rel = pathdiff:: diff_paths ( target, base_path) ;
366+ match rel {
367+ Some ( v) => {
368+ let s = v. to_string_lossy ( ) . to_string ( ) ;
369+ if cfg ! ( target_os = "windows" ) {
370+ s. replace ( '\\' , "/" )
371+ } else {
372+ s
340373 }
341- None => f. to_string ( ) ,
342374 }
375+ None => f. to_string ( ) ,
376+ }
377+ }
378+
379+ impl SourceMapGenConfig for SwcSourceMapConfig < ' _ > {
380+ fn file_name_to_source ( & self , f : & FileName ) -> String {
381+ map_file_name_to_source ( self . source_file_name , self . output_path , f)
343382 }
344383
345384 fn name_for_bytepos ( & self , pos : BytePos ) -> Option < & str > {
346385 self . names . get ( & pos) . map ( |v| & * * v)
347386 }
348387
388+ fn for_each_additional_name ( & self , op : & mut dyn FnMut ( & str ) ) {
389+ for name in self . additional_names {
390+ op ( name) ;
391+ }
392+ }
393+
349394 fn inline_sources_content ( & self , _: & FileName ) -> bool {
350395 self . inline_sources_content
351396 }
0 commit comments