mirror of
https://github.com/selfxyz/self.git
synced 2026-01-10 15:18:18 -05:00
* Add tree-shakeable exports * Migrate imports for tree-shakeable paths * Document ESM extension requirement * udpates * install new lock * yarn nice * build deps * save working index export no wildcard approach * save wip * fix building * add tree shaking doc and examples * sort package json files * update package.json * fix analyzing web * make sure that web is built * wip tree shaking * building works again. save wip logic * use granular imports * wip test * save wip * Remove hardcoded .d.ts files and setup automatic TypeScript declaration generation - Remove redundant constants.d.ts, types.d.ts, utils.d.ts files - Add build:types script to automatically generate TypeScript declarations - Update tsup config to disable DTS generation (handled separately) - Update .gitignore to prevent future commits of generated .d.ts files - Fixes import resolution errors in app by ensuring declarations are always generated * Add .gitignore rules for generated TypeScript declarations * ignore dts files * Remove redundant index.js re-export files - Remove constants.js, types.js, utils.js as they're redundant with tsup build - These were just re-exports pointing to dist files that tsup generates - package.json exports already point directly to built files - Update .gitignore to prevent future commits of these generated files - tsup handles all the building, no manual re-export files needed * save current wip fixes * add tsup config for web building * common prettier and fix imports * prettier * fix tests * implement level 3 tree shaking * improve splitting * optimize vite web building and prettier * remove comments * sort export params * feedback and fix pipelines * fix circuit-names path * fix test * fix building * sort * fix building * allow cursor to edit scripts * fix loadDocumentCatalog undefined * fix build settings * fix build settings * additional metro tree shaking * improved discovery script for xcode building * pr feedback and fix camelCasing * simplify shim setup * fix xcode building and add command to test building * remove comment * simplify
642 lines
20 KiB
JavaScript
Executable File
642 lines
20 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
const { execSync } = require('child_process');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
function formatBytes(bytes) {
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
if (bytes === 0) return '0 Bytes';
|
||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||
return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i];
|
||
}
|
||
|
||
function analyzeWebBundle() {
|
||
console.log('🕸️ Analyzing Web Bundle for Tree Shaking');
|
||
console.log('=========================================');
|
||
|
||
const distDir = path.join(__dirname, '..', 'web', 'dist');
|
||
const assetsDir = path.join(distDir, 'assets');
|
||
|
||
if (!fs.existsSync(distDir)) {
|
||
console.log('❌ Web build not found. Run "yarn web:build" first.');
|
||
return;
|
||
}
|
||
|
||
// Analyze chunk sizes - check both dist/ and dist/assets/
|
||
let files = [];
|
||
if (fs.existsSync(assetsDir)) {
|
||
files = fs
|
||
.readdirSync(assetsDir)
|
||
.filter(f => f.endsWith('.js'))
|
||
.map(f => path.join('assets', f));
|
||
}
|
||
if (files.length === 0) {
|
||
files = fs.readdirSync(distDir).filter(f => f.endsWith('.js'));
|
||
}
|
||
|
||
console.log('\n📦 JavaScript Chunks:');
|
||
let totalSize = 0;
|
||
|
||
files.forEach(file => {
|
||
const filePath = path.join(distDir, file);
|
||
const size = fs.statSync(filePath).size;
|
||
totalSize += size;
|
||
|
||
// Categorize chunks - use just the filename for categorization
|
||
const fileName = path.basename(file);
|
||
let category = '📄';
|
||
if (fileName.includes('vendor-')) category = '📚';
|
||
if (fileName.includes('screens-')) category = '🖥️ ';
|
||
if (fileName.includes('index')) category = '🏠';
|
||
|
||
// Show filename with size, highlighting large chunks
|
||
const sizeInfo = formatBytes(size);
|
||
const isLarge = size > 500 * 1024; // > 500KB
|
||
const displayName = fileName.padEnd(40);
|
||
const sizeDisplay = isLarge ? `⚠️ ${sizeInfo}` : sizeInfo;
|
||
|
||
console.log(`${category} ${displayName} ${sizeDisplay}`);
|
||
});
|
||
|
||
console.log(`\n📊 Total JavaScript: ${formatBytes(totalSize)}`);
|
||
|
||
// Check for source maps (indicates tree shaking info)
|
||
const sourceMaps = files.filter(f => path.basename(f).endsWith('.map'));
|
||
if (sourceMaps.length > 0) {
|
||
console.log(`📍 Source maps available: ${sourceMaps.length} files`);
|
||
}
|
||
|
||
// Analyze vendor chunks for common imports
|
||
const vendorChunks = files.filter(f => path.basename(f).includes('vendor-'));
|
||
if (vendorChunks.length > 0) {
|
||
console.log('\n🔍 Vendor Chunk Analysis:');
|
||
vendorChunks.forEach(chunk => {
|
||
const size = fs.statSync(path.join(distDir, chunk)).size;
|
||
const chunkName = path.basename(chunk);
|
||
console.log(` ${chunkName}: ${formatBytes(size)}`);
|
||
});
|
||
}
|
||
|
||
// Look for @selfxyz/common usage patterns
|
||
console.log('\n🌳 Tree Shaking Indicators:');
|
||
|
||
try {
|
||
// Check if chunks are split (good for tree shaking)
|
||
const nonVendorChunks = files.filter(
|
||
f => !path.basename(f).includes('vendor-'),
|
||
);
|
||
if (nonVendorChunks.length > 1) {
|
||
console.log('✅ Code splitting enabled - helps with tree shaking');
|
||
}
|
||
|
||
// Check for multiple vendor chunks (indicates good chunking strategy)
|
||
if (vendorChunks.length > 1) {
|
||
console.log('✅ Multiple vendor chunks - good separation of concerns');
|
||
}
|
||
|
||
// Identify large chunks that could benefit from tree shaking
|
||
const largeChunks = files.filter(f => {
|
||
const size = fs.statSync(path.join(distDir, f)).size;
|
||
return size > 1024 * 1024; // > 1MB
|
||
});
|
||
|
||
if (largeChunks.length > 0) {
|
||
console.log('\n⚠️ LARGE CHUNKS DETECTED:');
|
||
largeChunks.forEach(chunk => {
|
||
const size = fs.statSync(path.join(distDir, chunk)).size;
|
||
const chunkName = path.basename(chunk);
|
||
console.log(
|
||
` ${chunkName}: ${formatBytes(size)} - Consider tree shaking optimization`,
|
||
);
|
||
});
|
||
}
|
||
|
||
// Size-based heuristics
|
||
if (totalSize < 2 * 1024 * 1024) {
|
||
// Less than 2MB
|
||
console.log(
|
||
'✅ Reasonable total bundle size - tree shaking likely working',
|
||
);
|
||
} else {
|
||
console.log(
|
||
`⚠️ Large total bundle size (${formatBytes(totalSize)}) - significant tree shaking potential`,
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.log('❌ Could not analyze bundle details:', error.message);
|
||
}
|
||
}
|
||
|
||
function analyzeReactNativeBundle(platform) {
|
||
console.log(`📱 Analyzing React Native Bundle (${platform})`);
|
||
console.log('============================================');
|
||
|
||
// Use existing bundle analysis but with tree shaking focus
|
||
const bundleAnalyzeScript = path.join(__dirname, 'bundle-analyze-ci.cjs');
|
||
|
||
try {
|
||
console.log('🔨 Running bundle analysis...');
|
||
execSync(`node ${bundleAnalyzeScript} ${platform}`, {
|
||
stdio: 'inherit',
|
||
});
|
||
|
||
// Additional tree shaking specific analysis
|
||
const tmpDir = path.join(
|
||
require('os').tmpdir(),
|
||
'react-native-bundle-visualizer',
|
||
);
|
||
const reportPath = path.join(
|
||
tmpDir,
|
||
'OpenPassport',
|
||
'output',
|
||
'explorer.html',
|
||
);
|
||
|
||
if (fs.existsSync(reportPath)) {
|
||
console.log(`\n📊 Detailed bundle report: ${reportPath}`);
|
||
console.log('💡 Look for:');
|
||
console.log(' - Unused modules from @selfxyz/common');
|
||
console.log(' - Large vendor chunks that could be optimized');
|
||
console.log(' - Multiple copies of the same module');
|
||
}
|
||
} catch (error) {
|
||
console.log('❌ Bundle analysis failed:', error.message);
|
||
}
|
||
}
|
||
|
||
function categorizeImports(imports) {
|
||
const constants = [
|
||
'API_URL',
|
||
'API_URL_STAGING',
|
||
'countryCodes',
|
||
'commonNames',
|
||
'countries',
|
||
'PASSPORT_ATTESTATION_ID',
|
||
'ID_CARD_ATTESTATION_ID',
|
||
'DEFAULT_MAJORITY',
|
||
'CSCA_TREE_URL',
|
||
'DSC_TREE_URL',
|
||
'TREE_URL',
|
||
'TREE_URL_STAGING',
|
||
'PCR0_MANAGER_ADDRESS',
|
||
'RPC_URL',
|
||
'WS_DB_RELAYER',
|
||
];
|
||
|
||
const utils = [
|
||
'hash',
|
||
'flexiblePoseidon',
|
||
'customHasher',
|
||
'generateCommitment',
|
||
'generateNullifier',
|
||
'formatMrz',
|
||
'initPassportDataParsing',
|
||
'buildSMT',
|
||
'getLeafCscaTree',
|
||
'getLeafDscTree',
|
||
'generateCircuitInputsDSC',
|
||
'generateCircuitInputsRegister',
|
||
'generateCircuitInputsVCandDisclose',
|
||
'formatEndpoint',
|
||
'hashEndpointWithScope',
|
||
'stringToBigInt',
|
||
'bigIntToString',
|
||
'genMockIdDoc',
|
||
'generateMockDSC',
|
||
'genAndInitMockPassportData',
|
||
];
|
||
|
||
const types = [
|
||
'PassportData',
|
||
'DocumentCategory',
|
||
'CertificateData',
|
||
'PublicKeyDetailsECDSA',
|
||
'PublicKeyDetailsRSA',
|
||
'PassportMetadata',
|
||
'UserIdType',
|
||
'EndpointType',
|
||
'SelfApp',
|
||
'SelfAppDisclosureConfig',
|
||
'IdDocInput',
|
||
'Country3LetterCode',
|
||
];
|
||
|
||
const suggestions = [];
|
||
|
||
const constantImports = imports.filter(imp =>
|
||
constants.includes(imp.replace(/^type\s+/, '')),
|
||
);
|
||
const utilImports = imports.filter(imp =>
|
||
utils.includes(imp.replace(/^type\s+/, '')),
|
||
);
|
||
const typeImports = imports.filter(
|
||
imp =>
|
||
types.includes(imp.replace(/^type\s+/, '')) || imp.startsWith('type '),
|
||
);
|
||
|
||
if (constantImports.length > 0) {
|
||
suggestions.push({
|
||
category: 'constants',
|
||
imports: constantImports,
|
||
suggestion: `import { ${constantImports.join(', ')} } from '@selfxyz/common/constants';`,
|
||
});
|
||
}
|
||
|
||
if (utilImports.length > 0) {
|
||
suggestions.push({
|
||
category: 'utils',
|
||
imports: utilImports,
|
||
suggestion: `import { ${utilImports.join(', ')} } from '@selfxyz/common/utils';`,
|
||
});
|
||
}
|
||
|
||
if (typeImports.length > 0) {
|
||
suggestions.push({
|
||
category: 'types',
|
||
imports: typeImports,
|
||
suggestion: `import type { ${typeImports.map(t => t.replace(/^type\s+/, '')).join(', ')} } from '@selfxyz/common/types';`,
|
||
});
|
||
}
|
||
|
||
return suggestions;
|
||
}
|
||
|
||
function compareImportPatterns() {
|
||
console.log('\n🔬 Import Pattern Analysis');
|
||
console.log('==========================');
|
||
|
||
const srcDir = path.join(__dirname, '..', 'src');
|
||
|
||
if (!fs.existsSync(srcDir)) {
|
||
console.log('❌ Source directory not found');
|
||
return;
|
||
}
|
||
|
||
// Find TypeScript/JavaScript files
|
||
const findFiles = (dir, extensions = ['.ts', '.tsx', '.js', '.jsx']) => {
|
||
const files = [];
|
||
const items = fs.readdirSync(dir);
|
||
|
||
for (const item of items) {
|
||
const fullPath = path.join(dir, item);
|
||
if (fs.statSync(fullPath).isDirectory()) {
|
||
files.push(...findFiles(fullPath, extensions));
|
||
} else if (extensions.some(ext => item.endsWith(ext))) {
|
||
files.push(fullPath);
|
||
}
|
||
}
|
||
return files;
|
||
};
|
||
|
||
const files = findFiles(srcDir);
|
||
|
||
// Analyze import patterns
|
||
let totalFiles = 0;
|
||
let filesWithCommonImports = 0;
|
||
let starImports = 0;
|
||
let namedImports = 0;
|
||
let granularImports = 0;
|
||
|
||
const importPatterns = {
|
||
star: [],
|
||
mixed: [],
|
||
granular: [],
|
||
};
|
||
|
||
const fileConversionOpportunities = [];
|
||
|
||
files.forEach(file => {
|
||
const content = fs.readFileSync(file, 'utf8');
|
||
totalFiles++;
|
||
|
||
// Check for @selfxyz/common imports
|
||
const commonImportRegex = /import.*from\s+['"]@selfxyz\/common[^'"]*['"]/g;
|
||
const matches = content.match(commonImportRegex) || [];
|
||
|
||
if (matches.length > 0) {
|
||
filesWithCommonImports++;
|
||
|
||
const fileInfo = {
|
||
file: path.relative(srcDir, file),
|
||
imports: [],
|
||
conversionOpportunities: [],
|
||
priority: 0,
|
||
};
|
||
|
||
matches.forEach(match => {
|
||
if (match.includes('* as')) {
|
||
starImports++;
|
||
importPatterns.star.push({
|
||
file: path.relative(srcDir, file),
|
||
import: match.trim(),
|
||
});
|
||
fileInfo.imports.push({ type: 'star', import: match.trim() });
|
||
fileInfo.priority += 3; // High priority for star imports
|
||
} else if (
|
||
match.includes('/constants') ||
|
||
match.includes('/utils') ||
|
||
match.includes('/types')
|
||
) {
|
||
granularImports++;
|
||
importPatterns.granular.push({
|
||
file: path.relative(srcDir, file),
|
||
import: match.trim(),
|
||
});
|
||
fileInfo.imports.push({ type: 'granular', import: match.trim() });
|
||
} else {
|
||
namedImports++;
|
||
importPatterns.mixed.push({
|
||
file: path.relative(srcDir, file),
|
||
import: match.trim(),
|
||
});
|
||
fileInfo.imports.push({ type: 'mixed', import: match.trim() });
|
||
fileInfo.priority += 1; // Medium priority for mixed imports
|
||
|
||
// Analyze what specific imports this file has and suggest granular equivalents
|
||
const namedImportMatches = match.match(/import\s+\{([^}]+)\}/);
|
||
if (namedImportMatches) {
|
||
const imports = namedImportMatches[1]
|
||
.split(',')
|
||
.map(i => i.trim())
|
||
.filter(i => i && !i.includes('type'));
|
||
|
||
const suggestions = categorizeImports(imports);
|
||
if (suggestions.length > 0) {
|
||
fileInfo.conversionOpportunities = suggestions;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
if (fileInfo.priority > 0) {
|
||
fileConversionOpportunities.push(fileInfo);
|
||
}
|
||
}
|
||
});
|
||
|
||
console.log(`📁 Analyzed ${totalFiles} files`);
|
||
console.log(`📦 Files importing @selfxyz/common: ${filesWithCommonImports}`);
|
||
console.log(`⭐ Star imports (import *): ${starImports}`);
|
||
console.log(`📝 Named imports: ${namedImports}`);
|
||
console.log(`🎯 Granular imports: ${granularImports}`);
|
||
|
||
// Show recommendations
|
||
console.log('\n💡 OPTIMIZATION OPPORTUNITIES:');
|
||
|
||
if (starImports > 0) {
|
||
console.log(
|
||
`❌ Found ${starImports} star imports - these prevent tree shaking`,
|
||
);
|
||
if (importPatterns.star.length <= 5) {
|
||
console.log(' Examples:');
|
||
importPatterns.star.slice(0, 5).forEach(item => {
|
||
console.log(` 📄 ${item.file}: ${item.import}`);
|
||
});
|
||
}
|
||
}
|
||
|
||
if (namedImports > granularImports) {
|
||
console.log(
|
||
`⚠️ More mixed imports (${namedImports}) than granular (${granularImports})`,
|
||
);
|
||
console.log(
|
||
' Consider using granular imports like "@selfxyz/common/constants"',
|
||
);
|
||
}
|
||
|
||
if (granularImports > 0) {
|
||
console.log(`✅ Good: ${granularImports} granular imports found`);
|
||
}
|
||
|
||
// Calculate tree shaking score
|
||
const totalImports = starImports + namedImports + granularImports;
|
||
let score = 0;
|
||
if (totalImports > 0) {
|
||
score =
|
||
((granularImports * 100 + namedImports * 50) / (totalImports * 100)) *
|
||
100;
|
||
console.log(`\n📊 Tree Shaking Score: ${score.toFixed(1)}%`);
|
||
|
||
if (score < 50) {
|
||
console.log('🔴 Poor - Many star imports detected');
|
||
} else if (score < 80) {
|
||
console.log('🟡 Good - Mix of import patterns');
|
||
} else {
|
||
console.log('🟢 Excellent - Mostly granular imports');
|
||
}
|
||
}
|
||
|
||
// Show detailed conversion opportunities
|
||
if (fileConversionOpportunities.length > 0) {
|
||
console.log('\n🎯 CONVERSION OPPORTUNITIES BY IMPACT:');
|
||
console.log('=====================================');
|
||
|
||
// Group files by opportunity type
|
||
const opportunityGroups = {
|
||
highImpact: fileConversionOpportunities.filter(
|
||
f => f.imports.length >= 2,
|
||
),
|
||
constantsOnly: fileConversionOpportunities.filter(
|
||
f =>
|
||
f.conversionOpportunities.some(opp => opp.category === 'constants') &&
|
||
f.conversionOpportunities.length === 1,
|
||
),
|
||
utilsOnly: fileConversionOpportunities.filter(
|
||
f =>
|
||
f.conversionOpportunities.some(opp => opp.category === 'utils') &&
|
||
f.conversionOpportunities.length === 1,
|
||
),
|
||
typesOnly: fileConversionOpportunities.filter(
|
||
f =>
|
||
f.conversionOpportunities.some(opp => opp.category === 'types') &&
|
||
f.conversionOpportunities.length === 1,
|
||
),
|
||
mixedCategories: fileConversionOpportunities.filter(
|
||
f => f.conversionOpportunities.length > 1,
|
||
),
|
||
needsAnalysis: fileConversionOpportunities.filter(
|
||
f => f.conversionOpportunities.length === 0,
|
||
),
|
||
};
|
||
|
||
// Show High Impact Opportunities (multiple imports)
|
||
if (opportunityGroups.highImpact.length > 0) {
|
||
console.log(
|
||
'\n🚀 HIGH IMPACT OPPORTUNITIES (Multiple imports per file):',
|
||
);
|
||
opportunityGroups.highImpact
|
||
.sort((a, b) => b.imports.length - a.imports.length)
|
||
.forEach((fileInfo, index) => {
|
||
console.log(
|
||
`\n${index + 1}. 📄 ${fileInfo.file} (${fileInfo.imports.length} imports)`,
|
||
);
|
||
|
||
fileInfo.imports
|
||
.filter(imp => imp.type === 'mixed')
|
||
.forEach(imp => {
|
||
console.log(` ⚠️ ${imp.import}`);
|
||
});
|
||
|
||
if (fileInfo.conversionOpportunities.length > 0) {
|
||
console.log(' ✅ Convert to:');
|
||
fileInfo.conversionOpportunities.forEach(suggestion => {
|
||
console.log(` ${suggestion.suggestion}`);
|
||
});
|
||
}
|
||
|
||
const estimatedImprovement = fileInfo.imports.length * 2.5;
|
||
console.log(
|
||
` 📈 Estimated score improvement: +${estimatedImprovement.toFixed(1)}%`,
|
||
);
|
||
});
|
||
}
|
||
|
||
// Show by Category for easier batch conversion
|
||
if (opportunityGroups.constantsOnly.length > 0) {
|
||
console.log('\n🔧 CONSTANTS CONVERSION OPPORTUNITIES:');
|
||
console.log(' (Convert these together for consistency)');
|
||
opportunityGroups.constantsOnly.forEach(fileInfo => {
|
||
const suggestion = fileInfo.conversionOpportunities.find(
|
||
opp => opp.category === 'constants',
|
||
);
|
||
console.log(` 📄 ${fileInfo.file}`);
|
||
console.log(` ${suggestion.suggestion}`);
|
||
});
|
||
}
|
||
|
||
if (opportunityGroups.utilsOnly.length > 0) {
|
||
console.log('\n⚙️ UTILS CONVERSION OPPORTUNITIES:');
|
||
console.log(' (Convert these together for consistency)');
|
||
opportunityGroups.utilsOnly.forEach(fileInfo => {
|
||
const suggestion = fileInfo.conversionOpportunities.find(
|
||
opp => opp.category === 'utils',
|
||
);
|
||
console.log(` 📄 ${fileInfo.file}`);
|
||
console.log(` ${suggestion.suggestion}`);
|
||
});
|
||
}
|
||
|
||
if (opportunityGroups.typesOnly.length > 0) {
|
||
console.log('\n🏷️ TYPES CONVERSION OPPORTUNITIES:');
|
||
console.log(' (Convert these together for consistency)');
|
||
opportunityGroups.typesOnly.forEach(fileInfo => {
|
||
const suggestion = fileInfo.conversionOpportunities.find(
|
||
opp => opp.category === 'types',
|
||
);
|
||
console.log(` 📄 ${fileInfo.file}`);
|
||
console.log(` ${suggestion.suggestion}`);
|
||
});
|
||
}
|
||
|
||
if (opportunityGroups.mixedCategories.length > 0) {
|
||
console.log('\n🔀 MIXED CATEGORY OPPORTUNITIES:');
|
||
console.log(' (Files importing from multiple categories)');
|
||
opportunityGroups.mixedCategories.forEach(fileInfo => {
|
||
console.log(` 📄 ${fileInfo.file}`);
|
||
fileInfo.conversionOpportunities.forEach(suggestion => {
|
||
console.log(` ${suggestion.suggestion}`);
|
||
});
|
||
});
|
||
}
|
||
|
||
if (opportunityGroups.needsAnalysis.length > 0) {
|
||
console.log('\n❓ NEEDS MANUAL ANALYSIS:');
|
||
console.log(' (Imports not automatically categorized)');
|
||
opportunityGroups.needsAnalysis.forEach(fileInfo => {
|
||
console.log(` 📄 ${fileInfo.file}`);
|
||
fileInfo.imports
|
||
.filter(imp => imp.type === 'mixed')
|
||
.forEach(imp => {
|
||
console.log(` ${imp.import}`);
|
||
});
|
||
});
|
||
}
|
||
|
||
// Summary stats
|
||
console.log('\n📈 CONVERSION SUMMARY:');
|
||
console.log(
|
||
`🚀 High Impact: ${opportunityGroups.highImpact.length} files (multiple imports each)`,
|
||
);
|
||
console.log(
|
||
`🔧 Constants Only: ${opportunityGroups.constantsOnly.length} files`,
|
||
);
|
||
console.log(`⚙️ Utils Only: ${opportunityGroups.utilsOnly.length} files`);
|
||
console.log(`🏷️ Types Only: ${opportunityGroups.typesOnly.length} files`);
|
||
console.log(
|
||
`🔀 Mixed Categories: ${opportunityGroups.mixedCategories.length} files`,
|
||
);
|
||
console.log(
|
||
`❓ Needs Analysis: ${opportunityGroups.needsAnalysis.length} files`,
|
||
);
|
||
|
||
const potentialScoreImprovement = Math.min(
|
||
95,
|
||
score +
|
||
opportunityGroups.highImpact.length * 5 +
|
||
fileConversionOpportunities.length * 2,
|
||
);
|
||
console.log(
|
||
`🎯 Potential score after conversion: ~${potentialScoreImprovement.toFixed(1)}%`,
|
||
);
|
||
|
||
console.log('\n💡 RECOMMENDED CONVERSION ORDER:');
|
||
console.log('1. Start with HIGH IMPACT files (biggest score improvement)');
|
||
console.log('2. Batch convert by category (constants → utils → types)');
|
||
console.log('3. Handle mixed categories individually');
|
||
console.log('4. Manually analyze remaining files');
|
||
}
|
||
}
|
||
|
||
function main() {
|
||
const args = process.argv.slice(2);
|
||
const command = args[0];
|
||
|
||
console.log('🌳 Tree Shaking Bundle Analysis');
|
||
console.log('==============================');
|
||
|
||
switch (command) {
|
||
case 'web':
|
||
analyzeWebBundle();
|
||
break;
|
||
case 'android':
|
||
case 'ios':
|
||
analyzeReactNativeBundle(command);
|
||
break;
|
||
case 'imports':
|
||
compareImportPatterns();
|
||
break;
|
||
case 'all':
|
||
default:
|
||
compareImportPatterns();
|
||
console.log('\n');
|
||
analyzeWebBundle();
|
||
break;
|
||
}
|
||
|
||
if (!command || command === 'all') {
|
||
console.log('\n🚀 NEXT STEPS:');
|
||
console.log(
|
||
'1. Run "yarn test:tree-shaking" to test different import patterns',
|
||
);
|
||
console.log(
|
||
'2. Run "yarn analyze:tree-shaking android" for mobile bundle analysis',
|
||
);
|
||
console.log(
|
||
'3. Run "yarn analyze:tree-shaking web" after "yarn web:build"',
|
||
);
|
||
console.log(
|
||
'4. Check the generated reports for optimization opportunities',
|
||
);
|
||
}
|
||
}
|
||
|
||
if (require.main === module) {
|
||
main();
|
||
}
|
||
|
||
module.exports = {
|
||
analyzeWebBundle,
|
||
analyzeReactNativeBundle,
|
||
compareImportPatterns,
|
||
};
|