@@ -24,6 +24,7 @@ pub trait SourceMapGetter {
2424 ) -> Option < String > ;
2525}
2626
27+ #[ derive( Debug , PartialEq ) ]
2728pub enum SourceMapApplication {
2829 /// No mapping was applied, the location is unchanged.
2930 Unchanged ,
@@ -69,10 +70,6 @@ impl SourceMapper {
6970 }
7071 }
7172
72- pub ( crate ) fn has_user_sources ( & self ) -> bool {
73- self . getter . is_some ( )
74- }
75-
7673 /// Apply a source map to the passed location. If there is no source map for
7774 /// this location, or if the location remains unchanged after mapping, the
7875 /// changed values are returned.
@@ -154,27 +151,140 @@ impl SourceMapper {
154151 file_name : & str ,
155152 line_number : i64 ,
156153 ) -> Option < String > {
154+ if let Some ( maybe_source_line) =
155+ self . source_lines . get ( & ( file_name. to_string ( ) , line_number) )
156+ {
157+ return maybe_source_line. clone ( ) ;
158+ }
159+
160+ // TODO(bartlomieju): shouldn't we cache `None` values to avoid computing it each time, instead of early return?
161+ if let Some ( source_line) = self
162+ . loader
163+ . get_source_mapped_source_line ( file_name, ( line_number - 1 ) as usize )
164+ . filter ( |s| s. len ( ) <= Self :: MAX_SOURCE_LINE_LENGTH )
165+ {
166+ // Cache and return
167+ self . source_lines . insert (
168+ ( file_name. to_string ( ) , line_number) ,
169+ Some ( source_line. to_string ( ) ) ,
170+ ) ;
171+ return Some ( source_line) ;
172+ }
173+
174+ // TODO(bartlomieju): remove in deno_core 0.230.0
175+ // Fallback to a deprecated API
157176 let getter = self . getter . as_ref ( ) ?;
177+ let s = getter. get_source_line ( file_name, ( line_number - 1 ) as usize ) ;
178+ let maybe_source_line =
179+ s. filter ( |s| s. len ( ) <= Self :: MAX_SOURCE_LINE_LENGTH ) ;
180+ // Cache and return
181+ self . source_lines . insert (
182+ ( file_name. to_string ( ) , line_number) ,
183+ maybe_source_line. clone ( ) ,
184+ ) ;
185+ maybe_source_line
186+ }
187+ }
158188
159- self
160- . source_lines
161- . entry ( ( file_name. to_string ( ) , line_number) )
162- . or_insert_with ( || {
163- // Source lookup expects a 0-based line number, ours are 1-based.
164- if let Some ( source_line) = self
165- . loader
166- . get_source_mapped_source_line ( file_name, ( line_number - 1 ) as usize )
167- {
168- if source_line. len ( ) <= Self :: MAX_SOURCE_LINE_LENGTH {
169- Some ( source_line)
170- } else {
171- None
172- }
173- } else {
174- let s = getter. get_source_line ( file_name, ( line_number - 1 ) as usize ) ;
175- s. filter ( |s| s. len ( ) <= Self :: MAX_SOURCE_LINE_LENGTH )
176- }
177- } )
178- . clone ( )
189+ #[ cfg( test) ]
190+ mod tests {
191+ use anyhow:: Error ;
192+ use url:: Url ;
193+
194+ use super :: * ;
195+ use crate :: ascii_str;
196+ use crate :: ModuleCodeString ;
197+ use crate :: ModuleLoadResponse ;
198+ use crate :: ModuleSpecifier ;
199+ use crate :: RequestedModuleType ;
200+ use crate :: ResolutionKind ;
201+
202+ struct SourceMapLoaderContent {
203+ source_map : Option < ModuleCodeString > ,
204+ }
205+
206+ #[ derive( Default ) ]
207+ pub struct SourceMapLoader {
208+ map : HashMap < ModuleSpecifier , SourceMapLoaderContent > ,
209+ }
210+
211+ impl ModuleLoader for SourceMapLoader {
212+ fn resolve (
213+ & self ,
214+ _specifier : & str ,
215+ _referrer : & str ,
216+ _kind : ResolutionKind ,
217+ ) -> Result < ModuleSpecifier , Error > {
218+ unreachable ! ( )
219+ }
220+
221+ fn load (
222+ & self ,
223+ _module_specifier : & ModuleSpecifier ,
224+ _maybe_referrer : Option < & ModuleSpecifier > ,
225+ _is_dyn_import : bool ,
226+ _requested_module_type : RequestedModuleType ,
227+ ) -> ModuleLoadResponse {
228+ unreachable ! ( )
229+ }
230+
231+ fn get_source_map ( & self , file_name : & str ) -> Option < Vec < u8 > > {
232+ let url = Url :: parse ( file_name) . unwrap ( ) ;
233+ let content = self . map . get ( & url) ?;
234+ content
235+ . source_map
236+ . as_ref ( )
237+ . map ( |s| s. to_string ( ) . into_bytes ( ) )
238+ }
239+
240+ fn get_source_mapped_source_line (
241+ & self ,
242+ _file_name : & str ,
243+ _line_number : usize ,
244+ ) -> Option < String > {
245+ Some ( "fake source line" . to_string ( ) )
246+ }
247+ }
248+
249+ #[ test]
250+ fn test_source_mapper ( ) {
251+ let mut loader = SourceMapLoader :: default ( ) ;
252+ loader. map . insert (
253+ Url :: parse ( "file:///b.js" ) . unwrap ( ) ,
254+ SourceMapLoaderContent { source_map : None } ,
255+ ) ;
256+ loader. map . insert (
257+ Url :: parse ( "file:///a.ts" ) . unwrap ( ) ,
258+ SourceMapLoaderContent {
259+ source_map : Some ( ascii_str ! ( r#"{"version":3,"sources":["file:///a.ts"],"sourcesContent":["export function a(): string {\n return \"a\";\n}\n"],"names":[],"mappings":"AAAA,OAAO,SAAS;EACd,OAAO;AACT"}"# ) . into ( ) ) ,
260+ } ,
261+ ) ;
262+
263+ let mut source_mapper = SourceMapper :: new ( Rc :: new ( loader) , None ) ;
264+
265+ // Non-existent file
266+ let application =
267+ source_mapper. apply_source_map ( "file:///doesnt_exist.js" , 1 , 1 ) ;
268+ assert_eq ! ( application, SourceMapApplication :: Unchanged ) ;
269+
270+ // File with no source map
271+ let application = source_mapper. apply_source_map ( "file:///b.js" , 1 , 1 ) ;
272+ assert_eq ! ( application, SourceMapApplication :: Unchanged ) ;
273+
274+ // File with a source map
275+ let application = source_mapper. apply_source_map ( "file:///a.ts" , 1 , 21 ) ;
276+ assert_eq ! (
277+ application,
278+ SourceMapApplication :: LineAndColumn {
279+ line_number: 1 ,
280+ column_number: 17
281+ }
282+ ) ;
283+
284+ let line = source_mapper. get_source_line ( "file:///a.ts" , 1 ) . unwrap ( ) ;
285+ assert_eq ! ( line, "fake source line" ) ;
286+ // Get again to hit a cache
287+ let line = source_mapper. get_source_line ( "file:///a.ts" , 1 ) . unwrap ( ) ;
288+ assert_eq ! ( line, "fake source line" ) ;
179289 }
180290}
0 commit comments