@@ -14,7 +14,8 @@ const {
1414const FILE_ICONS = {
1515 ROOT : '📦' ,
1616 DIRECTORY : '📂' ,
17- FILE : '📄'
17+ FILE : '📄' ,
18+ SYMLINK : '🔗'
1819}
1920
2021/**
@@ -66,13 +67,28 @@ async function generateFileTree(startPath) {
6667/**
6768 * Recursively builds a tree structure of the file system
6869 * @param {string } itemPath - The path of the current item (file or directory)
70+ * @param {string } startPath - The original starting path
71+ * @param {Object } buildConfig - Configuration options
72+ * @param {number } depth - Current depth in the tree
73+ * @param {Set } [visitedPaths] - Set of already visited paths to detect loops
6974 * @returns {Promise<Object> } A promise that resolves to an object representing the tree
7075 */
71- async function buildTree ( itemPath , startPath , buildConfig , depth ) {
76+ async function buildTree ( itemPath , startPath , buildConfig , depth , visitedPaths = new Set ( ) ) {
7277 if ( buildConfig . maxDepth !== - 1 && depth >= buildConfig . maxDepth ) {
7378 return null
7479 }
7580
81+ const normalizedPath = path . resolve ( itemPath )
82+ if ( visitedPaths . has ( normalizedPath ) ) {
83+ return {
84+ name : path . basename ( itemPath ) + ' (symlink loop)' ,
85+ type : 'symlink' ,
86+ target : normalizedPath
87+ }
88+ }
89+
90+ visitedPaths . add ( normalizedPath )
91+
7692 if ( ! buildConfig . showHiddenFiles && isHiddenFile ( itemPath ) ) {
7793 return null
7894 }
@@ -81,14 +97,34 @@ async function buildTree(itemPath, startPath, buildConfig, depth) {
8197 return null
8298 }
8399
84- let stats
100+ let stats , lstat
85101 try {
86102 stats = await fs . stat ( itemPath )
103+ lstat = await fs . lstat ( itemPath )
87104 } catch ( error ) {
88105 console . warn ( `Warning: Skipping "${ itemPath } " due to error:` , error . message )
89106 return null
90107 }
91108 const name = path . basename ( itemPath )
109+ const isSymlink = lstat . isSymbolicLink ( )
110+
111+ if ( isSymlink ) {
112+ try {
113+ const target = await fs . readlink ( itemPath )
114+ return {
115+ name,
116+ type : 'symlink' ,
117+ target,
118+ size : buildConfig . showFileSize ? formatFileSize ( lstat . size ) : null
119+ }
120+ } catch ( error ) {
121+ return {
122+ name : name + ' (broken symlink)' ,
123+ type : 'symlink' ,
124+ target : 'unknown'
125+ }
126+ }
127+ }
92128
93129 if ( stats . isFile ( ) && buildConfig . directoryOnly ) {
94130 return null
@@ -104,7 +140,7 @@ async function buildTree(itemPath, startPath, buildConfig, depth) {
104140 try {
105141 const children = await fs . readdir ( itemPath )
106142 const childNodes = await Promise . all (
107- children . map ( child => buildTree ( path . join ( itemPath , child ) , startPath , buildConfig , depth + 1 ) )
143+ children . map ( child => buildTree ( path . join ( itemPath , child ) , startPath , buildConfig , depth + 1 , new Set ( visitedPaths ) ) )
108144 )
109145 const filteredNodes = childNodes . filter ( node => node !== null )
110146 const sortedNodes = sortNodes ( filteredNodes , buildConfig . sortOrder )
0 commit comments