Launch your command line tool with ease by handling configuration discovery, local module resolution, and process management.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
EventEmitter-based system providing lifecycle hooks for module preloading, loader registration, and process respawning with detailed status information and error handling.
Events emitted during module preloading operations, providing hooks for logging, error handling, and module registration customization.
/**
* Event emitted before attempting to preload a module
* @param moduleName - Name of the module being preloaded
*/
on('preload:before', (moduleName: string) => void): Liftoff;
/**
* Event emitted when a module is successfully preloaded
* @param moduleName - Name of the successfully loaded module
* @param module - The actual loaded module object
*/
on('preload:success', (moduleName: string, module: any) => void): Liftoff;
/**
* Event emitted when module preloading fails
* @param moduleName - Name of the module that failed to load
* @param error - Error object containing failure details
*/
on('preload:failure', (moduleName: string, error: Error) => void): Liftoff;Usage Examples:
const Liftoff = require('liftoff');
const MyApp = new Liftoff({
name: 'myapp',
extensions: {
'.coffee': 'coffee-script/register',
'.ts': 'ts-node/register'
}
});
// Set up preload event handlers
MyApp.on('preload:before', function(moduleName) {
console.log(`[PRELOAD] Attempting to load: ${moduleName}`);
});
MyApp.on('preload:success', function(moduleName, module) {
console.log(`[PRELOAD] Successfully loaded: ${moduleName}`);
// Custom module configuration after successful load
if (moduleName === 'coffee-script/register') {
// CoffeeScript is now available for .coffee files
console.log('CoffeeScript support enabled');
} else if (moduleName === 'babel-register') {
// Customize babel configuration
module.setDefaults({
presets: ['env'],
plugins: ['transform-runtime']
});
console.log('Babel configured with default presets');
}
});
MyApp.on('preload:failure', function(moduleName, error) {
console.error(`[PRELOAD] Failed to load: ${moduleName}`);
console.error(`[PRELOAD] Error: ${error.message}`);
// Provide fallback or alternative solutions
if (moduleName === 'coffee-script/register') {
console.warn('CoffeeScript support unavailable - .coffee files will not be processed');
} else if (moduleName === 'babel-register') {
console.warn('Babel transpilation unavailable - modern JS syntax may cause errors');
}
});
// Execute with preload modules
MyApp.prepare({
preload: ['babel-register', 'coffee-script/register', 'nonexistent-module']
}, function(env) {
MyApp.execute(env, function(env, argv) {
console.log('Application started with preloaded modules');
});
});Events emitted when file extension loaders are registered, allowing for monitoring and customization of extension handling.
/**
* Event emitted when a file extension loader is successfully registered
* @param loaderName - Name/path of the loader module
* @param module - The loaded loader module
*/
on('loader:success', (loaderName: string, module: any) => void): Liftoff;
/**
* Event emitted when a file extension loader fails to load
* @param loaderName - Name/path of the loader module that failed
* @param error - Error object containing failure details
*/
on('loader:failure', (loaderName: string, error: Error) => void): Liftoff;Usage Examples:
const MyApp = new Liftoff({
name: 'myapp',
extensions: {
'.js': null,
'.json': null,
'.coffee': 'coffee-script/register',
'.ts': 'ts-node/register',
'.yaml': 'js-yaml-loader'
},
configFiles: [
{ name: 'myappfile', path: '.', findUp: true }
]
});
// Monitor loader registration
MyApp.on('loader:success', function(loaderName, module) {
console.log(`[LOADER] Successfully registered: ${loaderName}`);
// Configure specific loaders after registration
if (loaderName === 'ts-node/register') {
console.log('TypeScript configuration loaded');
// ts-node is now handling .ts files
} else if (loaderName === 'js-yaml-loader') {
console.log('YAML configuration support enabled');
// YAML files can now be required
}
});
MyApp.on('loader:failure', function(loaderName, error) {
console.error(`[LOADER] Failed to register: ${loaderName}`);
console.error(`[LOADER] Reason: ${error.message}`);
// Handle missing optional loaders gracefully
if (loaderName === 'coffee-script/register') {
console.warn('CoffeeScript config files will not be supported');
console.warn('Install coffee-script package to enable .coffee configs');
} else if (loaderName === 'ts-node/register') {
console.warn('TypeScript config files will not be supported');
console.warn('Install ts-node package to enable .ts configs');
}
});
// Environment building will trigger loader events as configs are discovered
const env = MyApp.buildEnvironment();
console.log('Environment built with config support for:', Object.keys(MyApp.extensions));Events emitted when the process needs to be respawned with v8 flags, providing visibility into process management and flag handling.
/**
* Event emitted when Liftoff respawns the process for v8 flags
* @param flags - Array of v8 flags that triggered the respawn
* @param childProcess - The child process object
*/
on('respawn', (flags: string[], childProcess: object) => void): Liftoff;Usage Examples:
const Liftoff = require('liftoff');
const MyApp = new Liftoff({
name: 'myapp',
v8flags: ['--harmony', '--experimental-modules']
});
// Monitor process respawning
MyApp.on('respawn', function(flags, childProcess) {
console.log(`[RESPAWN] Process respawned with flags: ${flags.join(' ')}`);
console.log(`[RESPAWN] Child PID: ${childProcess.pid}`);
console.log(`[RESPAWN] Original PID: ${process.pid}`);
// Log respawn for debugging
if (flags.includes('--harmony')) {
console.log('ES6 harmony features enabled');
}
if (flags.includes('--experimental-modules')) {
console.log('Experimental ES modules enabled');
}
// Set up child process monitoring
childProcess.on('exit', function(code, signal) {
console.log(`[RESPAWN] Child process exited with code ${code}, signal ${signal}`);
});
});
// Execute - respawn will occur if v8flags detected in process.argv
MyApp.prepare({}, function(env) {
MyApp.execute(env, function(env, argv) {
console.log('Application running in respawned process (if respawn occurred)');
console.log('Process PID:', process.pid);
});
});
// Example with forced flags causing respawn
MyApp.prepare({}, function(env) {
MyApp.execute(env, ['--trace-deprecation'], function(env, argv) {
console.log('Forced respawn with --trace-deprecation flag');
});
});Comprehensive example showing how to use all Liftoff events together for complete lifecycle monitoring and customization.
const Liftoff = require('liftoff');
const chalk = require('chalk'); // For colored output (optional)
const MyTool = new Liftoff({
name: 'mytool',
extensions: {
'.js': null,
'.json': null,
'.coffee': 'coffee-script/register',
'.ts': 'ts-node/register',
'.babel.js': 'babel-register'
},
v8flags: ['--harmony'],
configFiles: [
{ name: '.mytoolrc', path: '~' },
{ name: 'mytoolfile', path: '.', findUp: true }
]
});
// Comprehensive event monitoring
let preloadCount = 0;
let loaderCount = 0;
let respawnOccurred = false;
MyTool.on('preload:before', function(moduleName) {
process.stdout.write(`⏳ Loading ${moduleName}...`);
});
MyTool.on('preload:success', function(moduleName, module) {
preloadCount++;
console.log(` ✅ Loaded ${moduleName}`);
// Module-specific post-load configuration
if (moduleName === 'babel-register') {
// Configure Babel after successful load
if (module.setDefaultOption) {
module.setDefaultOption('presets', ['env']);
console.log(' 📝 Configured Babel with env preset');
}
} else if (moduleName === 'coffee-script/register') {
console.log(' ☕ CoffeeScript support enabled');
}
});
MyTool.on('preload:failure', function(moduleName, error) {
console.log(` ❌ Failed to load ${moduleName}: ${error.message}`);
// Provide helpful installation hints
if (moduleName.includes('coffee-script')) {
console.log(' 💡 Try: npm install coffee-script');
} else if (moduleName.includes('babel')) {
console.log(' 💡 Try: npm install babel-register babel-preset-env');
} else if (moduleName.includes('ts-node')) {
console.log(' 💡 Try: npm install ts-node typescript');
}
});
MyTool.on('loader:success', function(loaderName, module) {
loaderCount++;
console.log(`🔧 Registered loader: ${loaderName}`);
});
MyTool.on('loader:failure', function(loaderName, error) {
console.warn(`⚠️ Failed to register loader: ${loaderName}`);
console.warn(` Reason: ${error.message}`);
});
MyTool.on('respawn', function(flags, childProcess) {
respawnOccurred = true;
console.log(`🔄 Respawned with flags: ${flags.join(' ')}`);
console.log(` Parent PID: ${process.pid} → Child PID: ${childProcess.pid}`);
});
// Execute the tool with full event monitoring
console.log('🚀 Starting MyTool...\n');
MyTool.prepare({
preload: ['babel-register', 'source-map-support/register']
}, function(env) {
console.log('\n📊 Environment Summary:');
console.log(` Working Directory: ${env.cwd}`);
console.log(` Config File: ${env.configPath || 'none found'}`);
console.log(` Local Module: ${env.modulePath || 'none found'}`);
console.log(` Preloaded Modules: ${preloadCount}`);
console.log(` Registered Loaders: ${loaderCount}`);
console.log(` Process Respawned: ${respawnOccurred ? 'yes' : 'no'}`);
MyTool.execute(env, function(env, argv) {
console.log('\n✨ MyTool is ready!');
console.log('CLI Arguments:', argv);
// Your tool's main logic here
if (env.configPath) {
try {
const config = require(env.configPath);
console.log('📄 Configuration loaded:', Object.keys(config));
} catch (e) {
console.error('❌ Failed to load config:', e.message);
}
}
});
});Best practices for handling errors in event listeners and preventing unhandled exceptions.
const MyApp = new Liftoff({ name: 'myapp' });
// Always handle errors in event listeners
MyApp.on('preload:failure', function(moduleName, error) {
// Log the error but don't throw - let execution continue
console.error(`Module ${moduleName} failed to load:`, error.message);
// Optionally exit for critical modules
if (moduleName === 'critical-module') {
console.error('Critical module failed - cannot continue');
process.exit(1);
}
});
MyApp.on('loader:failure', function(loaderName, error) {
// Loader failures are usually non-fatal
console.warn(`Optional loader ${loaderName} unavailable:`, error.message);
});
// Set up global error handling for unhandled exceptions
process.on('uncaughtException', function(error) {
console.error('Uncaught exception:', error.message);
console.error('Stack:', error.stack);
process.exit(1);
});
process.on('unhandledRejection', function(reason, promise) {
console.error('Unhandled promise rejection:', reason);
process.exit(1);
});