|
| 1 | +const fs = require('fs'); |
| 2 | +const path = require('path'); |
| 3 | + |
| 4 | +function countStoryFiles(directoryPath) { |
| 5 | + const storyFiles = []; |
| 6 | + const excludedStoryFiles = []; |
| 7 | + const errors = []; |
| 8 | + |
| 9 | + // Folders to exclude |
| 10 | + const excludeFolders = [ |
| 11 | + 'ffe-buttons-react', |
| 12 | + 'ffe-messages-react', |
| 13 | + 'ffe-feedback-react', |
| 14 | + 'ffe-chips-react', |
| 15 | + 'ffe-core', |
| 16 | + 'ffe-accordion-react', |
| 17 | + 'ffe-datepicker-react', |
| 18 | + 'ffe-form-react', |
| 19 | + 'ffe-cards-react', |
| 20 | + 'ffe-collapse-react', |
| 21 | + 'ffe-datepicker-react', |
| 22 | + 'ffe-grid-react', |
| 23 | + 'ffe-icons-react', |
| 24 | + 'ffe-lists-react', |
| 25 | + 'ffe-pagination-react', |
| 26 | + 'ffe-searchable-dropdown-react', |
| 27 | + 'ffe-spinner-react', |
| 28 | + ]; |
| 29 | + |
| 30 | + const colors = { |
| 31 | + pink: '\x1b[38;5;218m', |
| 32 | + blue: '\x1b[38;5;153m', |
| 33 | + green: '\x1b[38;5;151m', |
| 34 | + yellow: '\x1b[38;5;222m', |
| 35 | + purple: '\x1b[38;5;183m', |
| 36 | + cyan: '\x1b[38;5;159m', |
| 37 | + reset: '\x1b[0m', |
| 38 | + dim: '\x1b[2m', |
| 39 | + bold: '\x1b[1m', |
| 40 | + red: '\x1b[38;5;203m', |
| 41 | + }; |
| 42 | + |
| 43 | + // Function to get color based on progress |
| 44 | + function getProgressColor(progress) { |
| 45 | + // Define color stops from red to yellow to green |
| 46 | + const colorStops = [ |
| 47 | + { percent: 0, color: '203' }, // Light red |
| 48 | + { percent: 50, color: '222' }, // Light yellow |
| 49 | + { percent: 100, color: '151' }, // Light green |
| 50 | + ]; |
| 51 | + |
| 52 | + // Find the two color stops we're between |
| 53 | + let start = colorStops[0]; |
| 54 | + let end = colorStops[1]; |
| 55 | + |
| 56 | + for (let i = 0; i < colorStops.length - 1; i++) { |
| 57 | + if ( |
| 58 | + progress >= colorStops[i].percent && |
| 59 | + progress <= colorStops[i + 1].percent |
| 60 | + ) { |
| 61 | + start = colorStops[i]; |
| 62 | + end = colorStops[i + 1]; |
| 63 | + break; |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + // Calculate the color number based on progress between stops |
| 68 | + const range = end.percent - start.percent; |
| 69 | + const progressInRange = progress - start.percent; |
| 70 | + const percentage = progressInRange / range; |
| 71 | + |
| 72 | + const startColor = parseInt(start.color); |
| 73 | + const endColor = parseInt(end.color); |
| 74 | + const color = Math.round( |
| 75 | + startColor + (endColor - startColor) * percentage, |
| 76 | + ); |
| 77 | + |
| 78 | + return `\x1b[38;5;${color}m`; |
| 79 | + } |
| 80 | + |
| 81 | + function traverseExcludedDirectory(currentPath) { |
| 82 | + try { |
| 83 | + const files = fs.readdirSync(currentPath); |
| 84 | + |
| 85 | + for (const file of files) { |
| 86 | + const filePath = path.join(currentPath, file); |
| 87 | + const stats = fs.statSync(filePath); |
| 88 | + |
| 89 | + if (stats.isDirectory() && !file.startsWith('.')) { |
| 90 | + traverseExcludedDirectory(filePath); |
| 91 | + } else if (stats.isFile()) { |
| 92 | + if (file.match(/\.stories\.(tsx|jsx)$/)) { |
| 93 | + excludedStoryFiles.push({ |
| 94 | + name: file, |
| 95 | + path: filePath, |
| 96 | + }); |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + } catch (error) { |
| 101 | + errors.push(`Error accessing ${currentPath}: ${error.message}`); |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + function traverseDirectory(currentPath) { |
| 106 | + try { |
| 107 | + const files = fs.readdirSync(currentPath); |
| 108 | + |
| 109 | + for (const file of files) { |
| 110 | + const filePath = path.join(currentPath, file); |
| 111 | + const stats = fs.statSync(filePath); |
| 112 | + |
| 113 | + if (stats.isDirectory()) { |
| 114 | + // Check if it's an excluded folder at the root level |
| 115 | + const isExcludedFolder = excludeFolders.includes(file); |
| 116 | + |
| 117 | + // Skip node_modules and hidden directories |
| 118 | + if (file !== 'node_modules' && !file.startsWith('.')) { |
| 119 | + // If it's an excluded folder, store its stories separately |
| 120 | + if (isExcludedFolder) { |
| 121 | + traverseExcludedDirectory(filePath); |
| 122 | + } else { |
| 123 | + traverseDirectory(filePath); |
| 124 | + } |
| 125 | + } |
| 126 | + } else if (stats.isFile()) { |
| 127 | + // Check if file matches story file pattern |
| 128 | + if (file.match(/\.stories\.(tsx|jsx)$/)) { |
| 129 | + storyFiles.push({ |
| 130 | + name: file, |
| 131 | + path: filePath, |
| 132 | + }); |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + } catch (error) { |
| 137 | + errors.push(`Error accessing ${currentPath}: ${error.message}`); |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + traverseDirectory(directoryPath); |
| 142 | + |
| 143 | + // Calculate statistics |
| 144 | + const totalStories = storyFiles.length + excludedStoryFiles.length; |
| 145 | + const progressPercentage = ( |
| 146 | + (excludedStoryFiles.length / totalStories) * |
| 147 | + 100 |
| 148 | + ).toFixed(1); |
| 149 | + |
| 150 | + // Print results |
| 151 | + console.log( |
| 152 | + `\n${colors.purple}${colors.bold} Storybook Story Files Analysis${colors.reset}`, |
| 153 | + ); |
| 154 | + console.log( |
| 155 | + `${colors.dim}═══════════════════════════════${colors.reset}\n`, |
| 156 | + ); |
| 157 | + |
| 158 | + console.log(`${colors.blue} Regular Story Files:${colors.reset}`); |
| 159 | + storyFiles.forEach(file => { |
| 160 | + console.log( |
| 161 | + ` ${colors.dim}└─${colors.reset} ${colors.cyan}${file.name}${colors.reset} ${colors.dim}(${file.path})${colors.reset}`, |
| 162 | + ); |
| 163 | + }); |
| 164 | + |
| 165 | + console.log( |
| 166 | + `\n${colors.pink} Progress on "Semantiske farger":${colors.reset}`, |
| 167 | + ); |
| 168 | + console.log(`${colors.dim}──────────────────────────────${colors.reset}`); |
| 169 | + console.log( |
| 170 | + ` ${colors.yellow}Stories in excluded folders: ${excludedStoryFiles.length}${colors.reset}`, |
| 171 | + ); |
| 172 | + console.log( |
| 173 | + ` ${colors.yellow}Stories remaining: ${storyFiles.length}${colors.reset}`, |
| 174 | + ); |
| 175 | + console.log( |
| 176 | + ` ${colors.green}Progress: ${progressPercentage}% complete${colors.reset}`, |
| 177 | + ); |
| 178 | + |
| 179 | + // Create progress bar with gradient color |
| 180 | + const progressColor = getProgressColor(parseFloat(progressPercentage)); |
| 181 | + const progressBlocks = Math.floor(progressPercentage / 5); |
| 182 | + const emptyBlocks = 20 - progressBlocks; |
| 183 | + |
| 184 | + console.log( |
| 185 | + ` ${colors.bold}[${progressColor}${Array(progressBlocks) |
| 186 | + .fill('█') |
| 187 | + .join('')}${colors.dim}${Array(emptyBlocks) |
| 188 | + .fill('░') |
| 189 | + .join('')}${colors.reset}]`, |
| 190 | + ); |
| 191 | + |
| 192 | + console.log(`\n${colors.purple} Summary:${colors.reset}`); |
| 193 | + console.log(`${colors.dim}──────────────${colors.reset}`); |
| 194 | + console.log( |
| 195 | + ` ${colors.cyan}Total story files: ${totalStories}${colors.reset}`, |
| 196 | + ); |
| 197 | + console.log( |
| 198 | + ` ${colors.cyan}Excluded folders: ${colors.dim}${excludeFolders.join(', ')}${colors.reset}`, |
| 199 | + ); |
| 200 | + |
| 201 | + if (errors.length > 0) { |
| 202 | + console.log(`\n${colors.yellow}⚠️ Errors encountered:${colors.reset}`); |
| 203 | + errors.forEach(error => |
| 204 | + console.log(` ${colors.red}❌ ${error}${colors.reset}`), |
| 205 | + ); |
| 206 | + } |
| 207 | + |
| 208 | + return { |
| 209 | + totalFiles: storyFiles.length, |
| 210 | + excludedFilesCount: excludedStoryFiles.length, |
| 211 | + progressPercentage, |
| 212 | + files: storyFiles, |
| 213 | + excludedFiles: excludedStoryFiles, |
| 214 | + errors, |
| 215 | + }; |
| 216 | +} |
| 217 | + |
| 218 | +// Get the directory path from command line argument, or use current directory |
| 219 | +const targetDirectory = process.argv[2] || process.cwd(); |
| 220 | + |
| 221 | +// Run the counter |
| 222 | +countStoryFiles(targetDirectory); |
0 commit comments