@@ -58,7 +58,7 @@ var formatInterval = function(msg, interval) {
5858} ;
5959
6060var singleton ;
61- module . exports . create = function ( logger_ , config_ , jsFiles_ , sourcemapper_ ) {
61+ function create ( logger_ , config_ , jsFiles_ , sourcemapper_ ) {
6262 if ( singleton && ! config_ . forceNewAgent_ ) {
6363 return singleton ;
6464 }
@@ -377,22 +377,6 @@ module.exports.create = function(logger_, config_, jsFiles_, sourcemapper_) {
377377 return null ;
378378 }
379379
380- /**
381- * @param {!string } scriptPath path of a script
382- */
383- function pathToRegExp ( scriptPath ) {
384- // make sure the script path starts with a slash. This makes sure our
385- // regexp doesn't match monkey.js when the user asks to set a breakpoint
386- // in key.js
387- if ( path . sep === '/' || scriptPath . indexOf ( ':' ) === - 1 ) {
388- scriptPath = path . join ( path . sep , scriptPath ) ;
389- }
390- if ( path . sep !== '/' ) {
391- scriptPath = scriptPath . replace ( new RegExp ( '\\\\' , 'g' ) , '\\\\' ) ;
392- }
393- return new RegExp ( scriptPath + '$' ) ;
394- }
395-
396380 function setByRegExp ( scriptPath , line , column ) {
397381 var regexp = pathToRegExp ( scriptPath ) ;
398382 var num = v8 . setScriptBreakPointByRegExp ( regexp , line - 1 , column - 1 ) ;
@@ -433,17 +417,12 @@ module.exports.create = function(logger_, config_, jsFiles_, sourcemapper_) {
433417 var regexp = pathToRegExp ( scriptPath ) ;
434418 // Next try to match path.
435419 var matches = Object . keys ( fileStats ) . filter ( regexp . test . bind ( regexp ) ) ;
436- // Finally look for files with the same name regardless of path.
437- if ( matches . length !== 1 ) {
438- matches = Object . keys ( fileStats ) ;
439- var components = scriptPath . split ( path . sep ) ;
440- for ( var i = components . length - 1 ;
441- i >= 0 && matches . length > 1 ; i -- ) {
442- regexp = pathToRegExp ( components . slice ( i ) . join ( path . sep ) ) ;
443- matches = matches . filter ( regexp . test . bind ( regexp ) ) ;
444- }
420+ if ( matches . length === 1 ) {
421+ return matches ;
445422 }
446- return matches ;
423+
424+ // Finally look for files with the same name regardless of path.
425+ return findScriptsFuzzy ( scriptPath , Object . keys ( fileStats ) ) ;
447426 }
448427
449428 function onBreakpointHit ( breakpoint , callback , execState ) {
@@ -585,4 +564,66 @@ module.exports.create = function(logger_, config_, jsFiles_, sourcemapper_) {
585564 }
586565
587566 return singleton ;
567+ }
568+
569+ /**
570+ * @param {!string } scriptPath path of a script
571+ */
572+ function pathToRegExp ( scriptPath ) {
573+ // make sure the script path starts with a slash. This makes sure our
574+ // regexp doesn't match monkey.js when the user asks to set a breakpoint
575+ // in key.js
576+ if ( path . sep === '/' || scriptPath . indexOf ( ':' ) === - 1 ) {
577+ scriptPath = path . join ( path . sep , scriptPath ) ;
578+ }
579+ if ( path . sep !== '/' ) {
580+ scriptPath = scriptPath . replace ( new RegExp ( '\\\\' , 'g' ) , '\\\\' ) ;
581+ }
582+ // Escape '.' characters.
583+ scriptPath = scriptPath . replace ( '.' , '\\.' ) ;
584+ return new RegExp ( scriptPath + '$' ) ;
585+ }
586+
587+ /**
588+ * Given an list of available files and a script path to match, this function
589+ * tries to resolve the script to a (hopefully unique) match in the file list
590+ * disregarding the full path to the script. This can be useful because repo
591+ * file paths (that the UI has) may not necessarily be suffixes of the absolute
592+ * paths of the deployed files. This happens when the user deploys a
593+ * subdirectory of the repo.
594+ *
595+ * For example consider a file named `a/b.js` in the repo. If the
596+ * directory contents of `a` are deployed rather than the whole repo, we are not
597+ * going to have any file named `a/b.js` in the running Node process.
598+ *
599+ * We incrementally consider more components of the path until we find a unique
600+ * match, or return all the potential matches.
601+ *
602+ * @example findScriptsFuzzy('a/b.js', ['/d/b.js']) // -> ['/d/b.js']
603+ * @example findScriptsFuzzy('a/b.js', ['/c/b.js', '/d/b.js']); // -> []
604+ * @example findScriptsFuzzy('a/b.js', ['/x/a/b.js', '/y/a/b.js'])
605+ * // -> ['x/a/b.js', 'y/a/b.js']
606+ *
607+ * @param {string } scriptPath partial path to the script.
608+ * @param {array<string> } fileList an array of absolute paths of filenames
609+ * available.
610+ * @return {array<string> } list of files that match.
611+ */
612+ function findScriptsFuzzy ( scriptPath , fileList ) {
613+ var matches = fileList ;
614+ var components = scriptPath . split ( path . sep ) ;
615+ for ( var i = components . length - 1 ; i >= 0 ; i -- ) {
616+ var regexp = pathToRegExp ( components . slice ( i ) . join ( path . sep ) ) ;
617+ matches = matches . filter ( regexp . test . bind ( regexp ) ) ;
618+ if ( matches . length <= 1 ) {
619+ break ; // Either no matches, or we found a unique match.
620+ }
621+ }
622+ return matches ;
623+ }
624+
625+ module . exports = {
626+ create : create ,
627+ // Exposed for unit testing.
628+ findScriptsFuzzy : findScriptsFuzzy
588629} ;
0 commit comments