UNPKG

codebase-asset-optimizer

Version:

Professional CLI development tool for optimizing and managing assets in codebases. Detects unused assets, optimizes images to WebP, optimizes videos, and automatically replaces asset references. GIFs are preserved unchanged to maintain animation functiona

237 lines • 10.3 kB
#!/usr/bin/env node /** * Post-install script for codebase-asset-optimizer * Prompts users to add convenient npm scripts to their package.json */ import fs from 'fs-extra'; import path from 'path'; import inquirer from 'inquirer'; import chalk from 'chalk'; const SUGGESTED_SCRIPTS = { 'optimize:assets': 'npx asset-optimizer optimize', 'optimize:assets:dry': 'npx asset-optimizer optimize --dry-run', 'audit:assets': 'npx asset-optimizer audit', 'clean:assets': 'npx asset-optimizer clean', 'assets:interactive': 'npx asset-optimizer interactive', 'benchmark:assets': 'node node_modules/codebase-asset-optimizer/dist/scripts/benchmark.js' }; const SCRIPT_DESCRIPTIONS = { 'optimize:assets': 'Optimize all assets (images → WebP, video compression)', 'optimize:assets:dry': 'Preview what would be optimized (safe, no changes)', 'audit:assets': 'Analyze asset usage and optimization opportunities', 'clean:assets': 'Remove unused assets from your project', 'assets:interactive': 'Interactive mode with guided optimization steps', 'benchmark:assets': 'Show performance impact and potential savings' }; async function findPackageJson() { // Look for package.json starting from current directory and going up let currentDir = process.cwd(); const root = path.parse(currentDir).root; // For Turborepo environments, also check common workspace patterns const potentialDirs = [ currentDir, process.env.INIT_CWD || '', // npm sets this to the directory where npm was invoked process.env.PWD || '', ...process.argv.slice(2).filter(arg => !arg.startsWith('-') && fs.existsSync(arg)) ].filter(Boolean); // Check all potential directories first for (const dir of potentialDirs) { if (await fs.pathExists(dir)) { const packageJsonPath = path.join(dir, 'package.json'); if (await fs.pathExists(packageJsonPath)) { try { const pkg = await fs.readJson(packageJsonPath); // Verify this is a real project (not just node_modules) if (pkg.name && !pkg.name.startsWith('_') && !dir.includes('node_modules')) { return packageJsonPath; } } catch (e) { // Continue searching } } } } // Fallback to traditional directory traversal while (currentDir !== root) { const packageJsonPath = path.join(currentDir, 'package.json'); if (await fs.pathExists(packageJsonPath)) { try { const pkg = await fs.readJson(packageJsonPath); // Avoid node_modules directories if (pkg.name && !currentDir.includes('node_modules')) { return packageJsonPath; } } catch (e) { // Continue searching } } currentDir = path.dirname(currentDir); } return null; } async function detectEnvironmentContext(packageJsonPath) { const projectDir = path.dirname(packageJsonPath); // Check if this is the asset optimizer development environment const isAssetOptimizerDev = await fs.pathExists(path.join(projectDir, 'src', 'cli.ts')); // Check if there's a test-project directory const hasTestProject = await fs.pathExists(path.join(projectDir, 'test-project')); // Check for common asset directories const commonAssetDirs = ['public', 'assets', 'static', 'src/assets', 'public/assets']; const assetDirs = []; for (const dir of commonAssetDirs) { const fullPath = path.join(projectDir, dir); if (await fs.pathExists(fullPath)) { assetDirs.push(dir); } } return { isAssetOptimizerDev, hasTestProject, assetDirs, projectDir }; } async function main() { // Check if we're in the development environment (skip postinstall) const packageJsonPath = await findPackageJson(); if (packageJsonPath) { const context = await detectEnvironmentContext(packageJsonPath); if (context.isAssetOptimizerDev) { // Skip postinstall in development environment return; } } console.log('\nšŸŽ‰ Thank you for installing Codebase Asset Optimizer!\n'); // Find package.json if (!packageJsonPath) { console.log('šŸ“¦ No package.json found in current directory or parent directories.'); console.log(' You can still use the tool directly with: npx asset-optimizer\n'); return; } console.log(`šŸ“¦ Found package.json: ${path.relative(process.cwd(), packageJsonPath)}`); // Detect environment context const context = await detectEnvironmentContext(packageJsonPath); // Provide contextual suggestions if (context.isAssetOptimizerDev) { console.log(chalk.blue('šŸ”§ Development Environment Detected')); if (context.hasTestProject) { console.log(chalk.gray(' Try: npm run audit:assets test-project')); console.log(chalk.gray(' Try: npm run optimize:assets:dry test-project')); } } else if (context.assetDirs.length > 0) { console.log(chalk.blue(`šŸŽÆ Found asset directories: ${context.assetDirs.join(', ')}`)); console.log(chalk.gray(' Ready to optimize your assets!')); } // Read existing package.json let packageJson; try { packageJson = await fs.readJson(packageJsonPath); } catch (error) { console.log(chalk.red('āŒ Error reading package.json:', error)); return; } // Check which scripts already exist const existingScripts = packageJson.scripts || {}; const availableScripts = Object.entries(SUGGESTED_SCRIPTS).filter(([scriptName]) => !existingScripts[scriptName]); if (availableScripts.length === 0) { console.log(chalk.green('āœ… All suggested scripts already exist in your package.json!')); console.log(chalk.gray('\nYou can run:')); Object.keys(SUGGESTED_SCRIPTS).forEach(scriptName => { console.log(chalk.cyan(` npm run ${scriptName}`)); }); // Provide contextual next steps if (context.hasTestProject) { console.log(chalk.blue('\nšŸ’” Test project detected! Try:')); console.log(chalk.cyan(' npm run audit:assets test-project')); } else if (context.assetDirs.length > 0) { console.log(chalk.blue('\nšŸ’” Quick start:')); console.log(chalk.cyan(' npm run audit:assets')); } return; } // Show UX benefits console.log(chalk.blue('\nšŸš€ Would you like to add convenient npm scripts to your package.json?')); console.log(chalk.gray(' This will let your team run optimization commands easily:\n')); availableScripts.forEach(([scriptName, command]) => { console.log(chalk.white(` npm run ${chalk.cyan(scriptName)}`)); console.log(chalk.gray(` └─ ${SCRIPT_DESCRIPTIONS[scriptName]}\n`)); }); // Prompt user const { addScripts, selectedScripts } = await inquirer.prompt([ { type: 'confirm', name: 'addScripts', message: 'Add these npm scripts to your package.json?', default: true }, { type: 'checkbox', name: 'selectedScripts', message: 'Which scripts would you like to add?', choices: availableScripts.map(([scriptName, command]) => ({ name: `${scriptName} - ${SCRIPT_DESCRIPTIONS[scriptName]}`, value: scriptName, checked: true })), when: (answers) => answers.addScripts } ]); if (!addScripts) { console.log(chalk.yellow('\nā­ļø Skipped adding npm scripts.')); console.log(chalk.gray(' You can still use the tool directly:')); console.log(chalk.cyan(' npx asset-optimizer interactive\n')); return; } // Add selected scripts if (!packageJson.scripts) { packageJson.scripts = {}; } selectedScripts.forEach((scriptName) => { packageJson.scripts[scriptName] = SUGGESTED_SCRIPTS[scriptName]; }); // Write updated package.json try { await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); console.log(chalk.green(`\nāœ… Added ${selectedScripts.length} npm scripts to package.json!`)); console.log(chalk.blue('\nšŸŽÆ Quick Start:')); selectedScripts.forEach((scriptName) => { console.log(chalk.cyan(` npm run ${scriptName}`)); }); // Contextual recommendations if (context.hasTestProject) { console.log(chalk.blue('\nšŸ’” Test project detected! Try these commands:')); console.log(chalk.cyan(' npm run audit:assets test-project')); console.log(chalk.gray(' └─ Analyze the test project assets')); console.log(chalk.cyan(' npm run optimize:assets:dry test-project')); console.log(chalk.gray(' └─ Preview optimizations safely')); } else { console.log(chalk.blue('\nšŸ’” Recommended first step:')); if (context.assetDirs.length > 0) { console.log(chalk.cyan(' npm run audit:assets')); console.log(chalk.gray(` └─ Analyze assets in: ${context.assetDirs.join(', ')}`)); } else { console.log(chalk.cyan(' npm run audit:assets')); console.log(chalk.gray(' └─ See what assets can be optimized in your project')); } } console.log(chalk.blue('\nšŸ›”ļø Always use dry-run first:')); console.log(chalk.cyan(' npm run optimize:assets:dry')); console.log(chalk.gray(' └─ Preview changes before applying them\n')); } catch (error) { console.log(chalk.red('āŒ Error writing package.json:', error)); } } // Run with error handling main().catch((error) => { console.error(chalk.red('āŒ Post-install script failed:', error)); process.exit(0); // Don't fail the npm install }); //# sourceMappingURL=postinstall.js.map