Monorepo for Aesthetic.Computer aesthetic.computer
at main 311 lines 8.5 kB view raw
1/** 2 * Dependency Analyzer for OBJKT Packaging 3 * Analyzes piece code to determine which dependencies are actually needed 4 * and which can be safely excluded to reduce package size. 5 */ 6 7export class DependencyAnalyzer { 8 constructor() { 9 // Dynamic dependencies that are loaded on-demand and safe to exclude 10 this.dynamicDependencies = { 11 // wasmboy - Game Boy emulator (19MB) 12 'wasmboy': { 13 patterns: [ 14 /sound\.gameboy/i, 15 /gameboy\./i, 16 /WasmBoy/i, 17 /initGameboy/i, 18 /\.gb$/i, 19 /\.gbc$/i 20 ], 21 description: 'Game Boy emulator' 22 }, 23 24 // @mediapipe - Hand tracking (25MB) 25 '@mediapipe': { 26 patterns: [ 27 /hand\./i, 28 /mediapipe/i, 29 /hand:/i, 30 /handtracking/i, 31 /gesture/i 32 ], 33 description: 'MediaPipe hand tracking' 34 }, 35 36 // aframe - WebVR framework (2MB) 37 'aframe': { 38 patterns: [ 39 /aframe/i, 40 /a-frame/i, 41 /WebVR/i, 42 /vr\s*mode/i, 43 /VRButton/i 44 ], 45 description: 'A-Frame WebVR' 46 }, 47 48 // web3 - Blockchain (1.4MB) 49 'web3': { 50 patterns: [ 51 /web3/i, 52 /ethereum/i, 53 /blockchain/i, 54 /crypto/i, 55 /wallet/i, 56 /metamask/i 57 ], 58 description: 'Web3 blockchain libraries' 59 }, 60 61 // gpt3-tokenizer - AI tokenization (3.5MB) 62 'gpt3-tokenizer': { 63 patterns: [ 64 /gpt/i, 65 /tokenizer/i, 66 /openai/i, 67 /ai\./i, 68 /llm/i, 69 /chatgpt/i 70 ], 71 description: 'GPT tokenizer' 72 }, 73 74 // webpxmux - WebP processing (8.4MB) 75 'webpxmux': { 76 patterns: [ 77 /webp/i, 78 /\.webp/i, 79 /webpxmux/i 80 ], 81 description: 'WebP muxing' 82 }, 83 84 // three - Three.js 3D graphics (1.5MB) 85 'three': { 86 patterns: [ 87 /system.*["']fps["']/i, 88 /system.*["']3d["']/i, 89 /THREE\./i, 90 /Form\(/i, 91 /CUBEL/i, 92 /form\(/i, 93 /3d\s*graphics/i 94 ], 95 description: 'Three.js 3D graphics (static import)' 96 }, 97 98 // geckos - UDP networking (0MB bundled, loaded dynamically) 99 'geckos': { 100 patterns: [ 101 /system.*["']world["']/i, 102 /multiplayer/i, 103 /udp/i, 104 /networking/i, 105 /geckos/i, 106 /udp:connect/i, 107 /udp:send/i, 108 /udp:disconnect/i 109 ], 110 description: 'Geckos UDP networking (static import)' 111 } 112 }; 113 114 // Static dependencies that might be conditionally needed 115 this.conditionalDependencies = { 116 }; 117 } 118 119 /** 120 * Analyze a piece to determine which dependencies can be excluded 121 * @param {string} pieceCode - The piece source code 122 * @param {string} pieceSystem - The piece system (if any) 123 * @param {Object} options - Analysis options 124 * @returns {Object} Analysis result with exclusions and recommendations 125 */ 126 analyzePiece(pieceCode, pieceSystem = '', options = {}) { 127 const result = { 128 exclusions: [], 129 required: [], 130 savings: 0, 131 analysis: {}, 132 recommendations: [] 133 }; 134 135 // Analyze dynamic dependencies (safe to exclude) 136 for (const [depName, config] of Object.entries(this.dynamicDependencies)) { 137 const isUsed = this.testDependencyUsage(pieceCode, pieceSystem, config.patterns); 138 139 result.analysis[depName] = { 140 used: isUsed, 141 type: 'dynamic', 142 description: config.description, 143 safe: true 144 }; 145 146 if (!isUsed) { 147 result.exclusions.push(depName); 148 result.savings += this.getDependencySize(depName); 149 } else { 150 result.required.push(depName); 151 } 152 } 153 154 // Analyze conditional static dependencies (informational for now) 155 for (const [depName, config] of Object.entries(this.conditionalDependencies)) { 156 const isUsed = this.testDependencyUsage(pieceCode, pieceSystem, config.patterns); 157 158 result.analysis[depName] = { 159 used: isUsed, 160 type: 'static', 161 description: config.description, 162 safe: false // Not yet implemented for exclusion 163 }; 164 165 if (isUsed) { 166 result.required.push(depName); 167 } 168 } 169 170 // Add recommendations based on analysis 171 this.generateRecommendations(result); 172 173 return result; 174 } 175 176 /** 177 * Test if a piece uses a dependency based on patterns 178 */ 179 testDependencyUsage(pieceCode, pieceSystem, patterns) { 180 const combinedText = `${pieceCode}\n${pieceSystem}`; 181 182 return patterns.some(pattern => { 183 if (pattern instanceof RegExp) { 184 return pattern.test(combinedText); 185 } 186 return combinedText.toLowerCase().includes(pattern.toLowerCase()); 187 }); 188 } 189 190 /** 191 * Get estimated size of a dependency in MB 192 */ 193 getDependencySize(depName) { 194 const sizes = { 195 '@mediapipe': 25, 196 'wasmboy': 19, 197 'webpxmux': 8.4, 198 'gpt3-tokenizer': 3.5, 199 'aframe': 2.0, 200 'web3': 1.4, 201 'three': 1.5, 202 'geckos': 0 // Loaded dynamically, no static bundle size 203 }; 204 205 return sizes[depName] || 0; 206 } 207 208 /** 209 * Generate exclusion patterns for bundleDepFiles 210 */ 211 generateExclusionPatterns(exclusions) { 212 const patterns = []; 213 214 for (const dep of exclusions) { 215 switch (dep) { 216 case 'wasmboy': 217 patterns.push('wasmboy/**'); 218 break; 219 case '@mediapipe': 220 patterns.push('@mediapipe/**'); 221 patterns.push('tasks-vision/**'); 222 break; 223 case 'aframe': 224 patterns.push('aframe*'); 225 break; 226 case 'web3': 227 patterns.push('web3/**'); 228 break; 229 case 'gpt3-tokenizer': 230 patterns.push('gpt3-tokenizer/**'); 231 break; 232 case 'webpxmux': 233 patterns.push('webpxmux/**'); 234 break; 235 case 'three': 236 patterns.push('three/**'); 237 break; 238 case 'geckos': 239 patterns.push('geckos.io-client.*.min.js'); 240 break; 241 } 242 } 243 244 return patterns; 245 } 246 247 /** 248 * Generate recommendations based on analysis 249 */ 250 generateRecommendations(result) { 251 const totalSavings = result.savings; 252 253 if (totalSavings > 30) { 254 result.recommendations.push(`🎉 Excellent! Can save ${totalSavings.toFixed(1)}MB (~${Math.round(totalSavings/65*100)}% reduction)`); 255 } else if (totalSavings > 10) { 256 result.recommendations.push(`✅ Good savings: ${totalSavings.toFixed(1)}MB reduction possible`); 257 } else if (totalSavings > 0) { 258 result.recommendations.push(`📉 Small savings: ${totalSavings.toFixed(1)}MB reduction possible`); 259 } else { 260 result.recommendations.push(`ℹ️ This piece uses most dependencies - limited savings available`); 261 } 262 263 // Specific recommendations 264 if (!result.analysis['wasmboy']?.used) { 265 result.recommendations.push('🎮 Game Boy emulator not needed - safe to exclude'); 266 } 267 268 if (!result.analysis['@mediapipe']?.used) { 269 result.recommendations.push('👋 Hand tracking not needed - safe to exclude'); 270 } 271 } 272 273 /** 274 * Generate a summary report 275 */ 276 generateReport(analysis) { 277 const lines = [ 278 `📊 Dependency Analysis Report`, 279 `================================`, 280 ``, 281 `💾 Potential Savings: ${analysis.savings.toFixed(1)}MB`, 282 `🚫 Dependencies to Exclude: ${analysis.exclusions.length}`, 283 `✅ Dependencies Required: ${analysis.required.length}`, 284 ``, 285 `📋 Exclusions:` 286 ]; 287 288 for (const dep of analysis.exclusions) { 289 const info = analysis.analysis[dep]; 290 const size = this.getDependencySize(dep); 291 lines.push(`${dep} (${size}MB) - ${info.description}`); 292 } 293 294 if (analysis.required.length > 0) { 295 lines.push(``, `📋 Required Dependencies:`); 296 for (const dep of analysis.required) { 297 const info = analysis.analysis[dep]; 298 const size = this.getDependencySize(dep); 299 const type = info.type === 'static' ? ' (static import)' : ''; 300 lines.push(`${dep} (${size}MB) - ${info.description}${type}`); 301 } 302 } 303 304 lines.push(``, `💡 Recommendations:`); 305 for (const rec of analysis.recommendations) { 306 lines.push(` ${rec}`); 307 } 308 309 return lines.join('\n'); 310 } 311}