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
JavaScript
/**
* 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