Monorepo for Aesthetic.Computer
aesthetic.computer
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}