1/** 2 * Web Worker for Folder Structure Analysis 3 * 4 * Performs folder scanning in background thread to prevent UI blocking 5 * for large folders (hundreds/thousands of files) 6 */ 7 8// Listen for messages from main thread 9self.addEventListener('message', async (event) => { 10 const { type, files } = event.data; 11 12 if (type === 'SCAN_FOLDER') { 13 try { 14 console.log('[FolderWorker] Starting scan of', files.length, 'files'); 15 16 // Build folder structure 17 const result = await analyzeFolderStructure(files); 18 19 // Send completion message 20 self.postMessage({ 21 type: 'SCAN_COMPLETE', 22 result 23 }); 24 25 } catch (error) { 26 console.error('[FolderWorker] Scan failed:', error); 27 self.postMessage({ 28 type: 'SCAN_ERROR', 29 error: error.message || 'Unknown error' 30 }); 31 } 32 } 33}); 34 35/** 36 * Analyze folder structure from file list 37 */ 38async function analyzeFolderStructure(files) { 39 // Build folder structure from files 40 const root = { 41 name: '', 42 path: '', 43 files: [], 44 children: [], 45 fileCount: 0, 46 totalSize: 0 47 }; 48 49 const folderMap = new Map(); 50 folderMap.set('', root); 51 52 let processedFiles = 0; 53 const totalFiles = files.length; 54 55 // Process each file and build hierarchy 56 for (let i = 0; i < files.length; i++) { 57 const file = files[i]; 58 const relativePath = file.webkitRelativePath || file.name; 59 const parts = relativePath.split('/'); 60 61 // Build folder hierarchy 62 let currentPath = ''; 63 for (let j = 0; j < parts.length - 1; j++) { 64 const folderPart = parts[j]; 65 const parentPath = currentPath; 66 currentPath = currentPath ? `${currentPath}/${folderPart}` : folderPart; 67 68 if (!folderMap.has(currentPath)) { 69 const newFolder = { 70 name: folderPart, 71 path: currentPath, 72 files: [], 73 children: [], 74 fileCount: 0, 75 totalSize: 0 76 }; 77 78 folderMap.set(currentPath, newFolder); 79 80 // Add to parent's children 81 const parent = folderMap.get(parentPath); 82 if (parent) { 83 parent.children.push(newFolder); 84 } 85 } 86 } 87 88 // Add file to its parent folder (files are already serialized objects) 89 const parentPath = parts.slice(0, -1).join('/'); 90 const parentFolder = folderMap.get(parentPath); 91 if (parentFolder) { 92 parentFolder.files.push(file); 93 } 94 95 // Send progress update every 50 files or if last file 96 processedFiles++; 97 if (processedFiles % 50 === 0 || processedFiles === totalFiles) { 98 const progress = Math.floor((processedFiles / totalFiles) * 90); // Reserve 90% for file processing 99 self.postMessage({ 100 type: 'SCAN_PROGRESS', 101 progress, 102 processedFiles, 103 totalFiles 104 }); 105 } 106 } 107 108 // Calculate totals recursively 109 const calculateTotals = (node) => { 110 node.fileCount = node.files.length; 111 node.totalSize = node.files.reduce((sum, file) => sum + file.size, 0); 112 113 for (const child of node.children) { 114 calculateTotals(child); 115 node.fileCount += child.fileCount; 116 node.totalSize += child.totalSize; 117 } 118 }; 119 120 calculateTotals(root); 121 122 // Calculate max depth 123 const calculateMaxDepth = (node, currentDepth = 0) => { 124 if (node.children.length === 0) { 125 return currentDepth; 126 } 127 return Math.max(...node.children.map(child => calculateMaxDepth(child, currentDepth + 1))); 128 }; 129 130 const maxDepth = calculateMaxDepth(root); 131 132 // Get root folder name from first file 133 const firstFile = files[0]; 134 const firstPath = firstFile.webkitRelativePath || firstFile.name; 135 const folderName = firstPath.split('/')[0] || 'Unknown Folder'; 136 137 // Count total folders 138 const totalFolders = folderMap.size - 1; // Exclude root 139 140 // Final progress update (100%) 141 self.postMessage({ 142 type: 'SCAN_PROGRESS', 143 progress: 100, 144 processedFiles: totalFiles, 145 totalFiles 146 }); 147 148 return { 149 folderName, 150 totalFiles: files.length, 151 totalFolders, 152 totalSize: root.totalSize, 153 maxDepth, 154 folderTree: root 155 }; 156} 157 158console.log('[FolderWorker] Worker initialized and ready');
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.