@@ -545,23 +545,64 @@ export async function build(
545545    } , 
546546  } 
547547
548-   const  mergeRollupError  =  ( e : RollupError )  =>  { 
548+   /** 
549+    * The stack string usually contains a copy of the message at the start of the stack. 
550+    * If the stack starts with the message, we remove it and just return the stack trace 
551+    * portion. Otherwise the original stack trace is used. 
552+    */ 
553+   function  extractStack ( e : RollupError )  { 
554+     const  {  stack,  name =  'Error' ,  message }  =  e 
555+ 
556+     // If we don't have a stack, not much we can do. 
557+     if  ( ! stack )  { 
558+       return  stack 
559+     } 
560+ 
561+     const  expectedPrefix  =  `${ name } ${ message }  
562+     if  ( stack . startsWith ( expectedPrefix ) )  { 
563+       return  stack . slice ( expectedPrefix . length ) 
564+     } 
565+ 
566+     return  stack 
567+   } 
568+ 
569+   /** 
570+    * Esbuild code frames have newlines at the start and end of the frame, rollup doesn't 
571+    * This function normalizes the frame to match the esbuild format which has more pleasing padding 
572+    */ 
573+   const  normalizeCodeFrame  =  ( frame : string )  =>  { 
574+     const  trimmedPadding  =  frame . replace ( / ^ \n | \n $ / g,  '' ) 
575+     return  `\n${ trimmedPadding }  
576+   } 
577+ 
578+   const  enhanceRollupError  =  ( e : RollupError )  =>  { 
579+     const  stackOnly  =  extractStack ( e ) 
580+ 
549581    let  msg  =  colors . red ( ( e . plugin  ? `[${ e . plugin }   : '' )  +  e . message ) 
550582    if  ( e . id )  { 
551583      msg  +=  `\nfile: ${ colors . cyan (  
552584        e . id  +  ( e . loc  ? `:${ e . loc . line } ${ e . loc . column }   : '' ) ,  
553585      ) }  `
554586    } 
555587    if  ( e . frame )  { 
556-       msg  +=  `\n`  +  colors . yellow ( e . frame ) 
588+       msg  +=  `\n`  +  colors . yellow ( normalizeCodeFrame ( e . frame ) ) 
589+     } 
590+ 
591+     e . message  =  msg 
592+ 
593+     // We are rebuilding the stack trace to include the more detailed message at the top. 
594+     // Previously this code was relying on mutating e.message changing the generated stack 
595+     // when it was accessed, but we don't have any guarantees that the error we are working 
596+     // with hasn't already had its stack accessed before we get here. 
597+     if  ( stackOnly  !==  undefined )  { 
598+       e . stack  =  `${ e . message } ${ stackOnly }  
557599    } 
558-     return  msg 
559600  } 
560601
561602  const  outputBuildError  =  ( e : RollupError )  =>  { 
562-     const   msg   =   mergeRollupError ( e ) 
603+     enhanceRollupError ( e ) 
563604    clearLine ( ) 
564-     config . logger . error ( msg ,  {  error : e  } ) 
605+     config . logger . error ( e . message ,  {  error : e  } ) 
565606  } 
566607
567608  let  bundle : RollupBuild  |  undefined 
@@ -727,7 +768,7 @@ export async function build(
727768    ) 
728769    return  Array . isArray ( outputs )  ? res  : res [ 0 ] 
729770  }  catch  ( e )  { 
730-     e . message   =   mergeRollupError ( e ) 
771+     enhanceRollupError ( e ) 
731772    clearLine ( ) 
732773    if  ( startTime )  { 
733774      config . logger . error ( 
0 commit comments