@@ -273,26 +273,130 @@ export function generateSyncSummary(results: FileSyncResult[]): SyncSummary {
273273 return summary ;
274274}
275275
276+ interface TreeNode {
277+ name : string ;
278+ children : Map < string , TreeNode > ;
279+ files : string [ ] ;
280+ action : FileAction ;
281+ }
282+
283+ /**
284+ * Builds a tree structure from file paths
285+ */
286+ function buildTree (
287+ files : { path : string ; action : FileAction } [ ]
288+ ) : Map < FileAction , TreeNode > {
289+ const actionTrees = new Map < FileAction , TreeNode > ( ) ;
290+
291+ for ( const { path, action } of files ) {
292+ if ( ! actionTrees . has ( action ) ) {
293+ actionTrees . set ( action , {
294+ name : "" ,
295+ children : new Map ( ) ,
296+ files : [ ] ,
297+ action,
298+ } ) ;
299+ }
300+
301+ const parts = path . split ( "/" ) ;
302+ const filename = parts . pop ( ) || "" ;
303+ let currentNode = actionTrees . get ( action ) ! ;
304+
305+ // Traverse or create the directory structure
306+ for ( const part of parts ) {
307+ if ( ! currentNode . children . has ( part ) ) {
308+ currentNode . children . set ( part , {
309+ name : part ,
310+ children : new Map ( ) ,
311+ files : [ ] ,
312+ action,
313+ } ) ;
314+ }
315+ currentNode = currentNode . children . get ( part ) ! ;
316+ }
317+
318+ // Add the file to the current directory
319+ if ( filename ) {
320+ currentNode . files . push ( filename ) ;
321+ }
322+ }
323+
324+ return actionTrees ;
325+ }
326+
276327/**
277- * Logs detailed information about file sync operations
328+ * Formats a tree node and its children as a string
329+ */
330+ function formatTreeNode (
331+ node : TreeNode ,
332+ isLast : boolean ,
333+ prefix : string [ ] ,
334+ output : string [ ]
335+ ) : void {
336+ // Skip root node if it's empty
337+ if ( node . name ) {
338+ const connector = isLast ? "└── " : "├── " ;
339+ output . push ( [ ...prefix , connector + node . name ] . join ( "" ) ) ;
340+ }
341+
342+ const newPrefix = [ ...prefix , isLast ? " " : "│ " ] ;
343+
344+ // Process directories first
345+ const sortedDirs = Array . from ( node . children . entries ( ) ) . sort (
346+ ( [ nameA ] , [ nameB ] ) => nameA . localeCompare ( nameB )
347+ ) ;
348+
349+ for ( let i = 0 ; i < sortedDirs . length ; i ++ ) {
350+ const dirEntry = sortedDirs [ i ] ;
351+ if ( ! dirEntry ) continue ;
352+ const [ _ , child ] = dirEntry ;
353+ const isLastChild = i === sortedDirs . length - 1 && node . files . length === 0 ;
354+ formatTreeNode ( child , isLastChild , newPrefix , output ) ;
355+ }
356+
357+ // Then process files
358+ const sortedFiles = [ ...node . files ] . sort ( ) ;
359+ for ( let i = 0 ; i < sortedFiles . length ; i ++ ) {
360+ const isLastFile = i === sortedFiles . length - 1 ;
361+ const connector = isLastFile ? "└── " : "├── " ;
362+ output . push ( [ ...newPrefix , connector + sortedFiles [ i ] ] . join ( "" ) ) ;
363+ }
364+ }
365+
366+ /**
367+ * Logs detailed information about file sync operations, grouped by action type
278368 */
279369export function logSyncDetails ( results : FileSyncResult [ ] ) : void {
280370 if ( ! results . length ) return ;
281371
282- logger . log ( "" ) ; // Empty line before details
283- logger . info ( "File synchronization details:" ) ;
284- logger . log ( "=" . repeat ( 80 ) ) ;
372+ const output : string [ ] = [ "File synchronization details:" , "=" . repeat ( 80 ) ] ;
285373
286- for ( const result of results ) {
287- if ( result . actionResult . action !== FileAction . NONE ) {
288- logger . log (
289- `${ result . actionResult . action . toString ( ) . padEnd ( 10 ) } ${ result . operation . relativeLocalPath } `
290- ) ;
291- }
374+ // Prepare files for tree building
375+ const files = results
376+ . filter ( result => result . actionResult . action !== FileAction . NONE )
377+ . map ( result => ( {
378+ path : result . operation . relativeLocalPath ,
379+ action : result . actionResult . action ,
380+ } ) ) ;
381+
382+ // Build tree structure
383+ const actionTrees = buildTree ( files ) ;
384+
385+ // Sort actions for consistent output
386+ const sortedActions = Array . from ( actionTrees . entries ( ) ) . sort (
387+ ( [ actionA ] , [ actionB ] ) => actionA . localeCompare ( actionB )
388+ ) ;
389+
390+ // Generate output
391+ for ( const actionTree of sortedActions ) {
392+ if ( ! actionTree ) continue ;
393+ const [ action , tree ] = actionTree ;
394+ output . push ( `\n${ action . toUpperCase ( ) } :` ) ;
395+ formatTreeNode ( tree , true , [ ] , output ) ;
292396 }
293397
294- logger . log ( "=" . repeat ( 80 ) ) ;
295- logger . log ( "" ) ; // Empty line after details
398+ output . push ( "\n" + "=" . repeat ( 80 ) ) ;
399+ logger . log ( output . join ( "\n" ) ) ;
296400}
297401
298402/**
0 commit comments