0
# Event System
1
2
EventEmitter-based system providing lifecycle hooks for module preloading, loader registration, and process respawning with detailed status information and error handling.
3
4
## Capabilities
5
6
### Preload Events
7
8
Events emitted during module preloading operations, providing hooks for logging, error handling, and module registration customization.
9
10
```javascript { .api }
11
/**
12
* Event emitted before attempting to preload a module
13
* @param moduleName - Name of the module being preloaded
14
*/
15
on('preload:before', (moduleName: string) => void): Liftoff;
16
17
/**
18
* Event emitted when a module is successfully preloaded
19
* @param moduleName - Name of the successfully loaded module
20
* @param module - The actual loaded module object
21
*/
22
on('preload:success', (moduleName: string, module: any) => void): Liftoff;
23
24
/**
25
* Event emitted when module preloading fails
26
* @param moduleName - Name of the module that failed to load
27
* @param error - Error object containing failure details
28
*/
29
on('preload:failure', (moduleName: string, error: Error) => void): Liftoff;
30
```
31
32
**Usage Examples:**
33
34
```javascript
35
const Liftoff = require('liftoff');
36
37
const MyApp = new Liftoff({
38
name: 'myapp',
39
extensions: {
40
'.coffee': 'coffee-script/register',
41
'.ts': 'ts-node/register'
42
}
43
});
44
45
// Set up preload event handlers
46
MyApp.on('preload:before', function(moduleName) {
47
console.log(`[PRELOAD] Attempting to load: ${moduleName}`);
48
});
49
50
MyApp.on('preload:success', function(moduleName, module) {
51
console.log(`[PRELOAD] Successfully loaded: ${moduleName}`);
52
53
// Custom module configuration after successful load
54
if (moduleName === 'coffee-script/register') {
55
// CoffeeScript is now available for .coffee files
56
console.log('CoffeeScript support enabled');
57
} else if (moduleName === 'babel-register') {
58
// Customize babel configuration
59
module.setDefaults({
60
presets: ['env'],
61
plugins: ['transform-runtime']
62
});
63
console.log('Babel configured with default presets');
64
}
65
});
66
67
MyApp.on('preload:failure', function(moduleName, error) {
68
console.error(`[PRELOAD] Failed to load: ${moduleName}`);
69
console.error(`[PRELOAD] Error: ${error.message}`);
70
71
// Provide fallback or alternative solutions
72
if (moduleName === 'coffee-script/register') {
73
console.warn('CoffeeScript support unavailable - .coffee files will not be processed');
74
} else if (moduleName === 'babel-register') {
75
console.warn('Babel transpilation unavailable - modern JS syntax may cause errors');
76
}
77
});
78
79
// Execute with preload modules
80
MyApp.prepare({
81
preload: ['babel-register', 'coffee-script/register', 'nonexistent-module']
82
}, function(env) {
83
MyApp.execute(env, function(env, argv) {
84
console.log('Application started with preloaded modules');
85
});
86
});
87
```
88
89
### Loader Events
90
91
Events emitted when file extension loaders are registered, allowing for monitoring and customization of extension handling.
92
93
```javascript { .api }
94
/**
95
* Event emitted when a file extension loader is successfully registered
96
* @param loaderName - Name/path of the loader module
97
* @param module - The loaded loader module
98
*/
99
on('loader:success', (loaderName: string, module: any) => void): Liftoff;
100
101
/**
102
* Event emitted when a file extension loader fails to load
103
* @param loaderName - Name/path of the loader module that failed
104
* @param error - Error object containing failure details
105
*/
106
on('loader:failure', (loaderName: string, error: Error) => void): Liftoff;
107
```
108
109
**Usage Examples:**
110
111
```javascript
112
const MyApp = new Liftoff({
113
name: 'myapp',
114
extensions: {
115
'.js': null,
116
'.json': null,
117
'.coffee': 'coffee-script/register',
118
'.ts': 'ts-node/register',
119
'.yaml': 'js-yaml-loader'
120
},
121
configFiles: [
122
{ name: 'myappfile', path: '.', findUp: true }
123
]
124
});
125
126
// Monitor loader registration
127
MyApp.on('loader:success', function(loaderName, module) {
128
console.log(`[LOADER] Successfully registered: ${loaderName}`);
129
130
// Configure specific loaders after registration
131
if (loaderName === 'ts-node/register') {
132
console.log('TypeScript configuration loaded');
133
// ts-node is now handling .ts files
134
} else if (loaderName === 'js-yaml-loader') {
135
console.log('YAML configuration support enabled');
136
// YAML files can now be required
137
}
138
});
139
140
MyApp.on('loader:failure', function(loaderName, error) {
141
console.error(`[LOADER] Failed to register: ${loaderName}`);
142
console.error(`[LOADER] Reason: ${error.message}`);
143
144
// Handle missing optional loaders gracefully
145
if (loaderName === 'coffee-script/register') {
146
console.warn('CoffeeScript config files will not be supported');
147
console.warn('Install coffee-script package to enable .coffee configs');
148
} else if (loaderName === 'ts-node/register') {
149
console.warn('TypeScript config files will not be supported');
150
console.warn('Install ts-node package to enable .ts configs');
151
}
152
});
153
154
// Environment building will trigger loader events as configs are discovered
155
const env = MyApp.buildEnvironment();
156
console.log('Environment built with config support for:', Object.keys(MyApp.extensions));
157
```
158
159
### Respawn Events
160
161
Events emitted when the process needs to be respawned with v8 flags, providing visibility into process management and flag handling.
162
163
```javascript { .api }
164
/**
165
* Event emitted when Liftoff respawns the process for v8 flags
166
* @param flags - Array of v8 flags that triggered the respawn
167
* @param childProcess - The child process object
168
*/
169
on('respawn', (flags: string[], childProcess: object) => void): Liftoff;
170
```
171
172
**Usage Examples:**
173
174
```javascript
175
const Liftoff = require('liftoff');
176
177
const MyApp = new Liftoff({
178
name: 'myapp',
179
v8flags: ['--harmony', '--experimental-modules']
180
});
181
182
// Monitor process respawning
183
MyApp.on('respawn', function(flags, childProcess) {
184
console.log(`[RESPAWN] Process respawned with flags: ${flags.join(' ')}`);
185
console.log(`[RESPAWN] Child PID: ${childProcess.pid}`);
186
console.log(`[RESPAWN] Original PID: ${process.pid}`);
187
188
// Log respawn for debugging
189
if (flags.includes('--harmony')) {
190
console.log('ES6 harmony features enabled');
191
}
192
if (flags.includes('--experimental-modules')) {
193
console.log('Experimental ES modules enabled');
194
}
195
196
// Set up child process monitoring
197
childProcess.on('exit', function(code, signal) {
198
console.log(`[RESPAWN] Child process exited with code ${code}, signal ${signal}`);
199
});
200
});
201
202
// Execute - respawn will occur if v8flags detected in process.argv
203
MyApp.prepare({}, function(env) {
204
MyApp.execute(env, function(env, argv) {
205
console.log('Application running in respawned process (if respawn occurred)');
206
console.log('Process PID:', process.pid);
207
});
208
});
209
210
// Example with forced flags causing respawn
211
MyApp.prepare({}, function(env) {
212
MyApp.execute(env, ['--trace-deprecation'], function(env, argv) {
213
console.log('Forced respawn with --trace-deprecation flag');
214
});
215
});
216
```
217
218
### Event-Driven Workflow Example
219
220
Comprehensive example showing how to use all Liftoff events together for complete lifecycle monitoring and customization.
221
222
```javascript
223
const Liftoff = require('liftoff');
224
const chalk = require('chalk'); // For colored output (optional)
225
226
const MyTool = new Liftoff({
227
name: 'mytool',
228
extensions: {
229
'.js': null,
230
'.json': null,
231
'.coffee': 'coffee-script/register',
232
'.ts': 'ts-node/register',
233
'.babel.js': 'babel-register'
234
},
235
v8flags: ['--harmony'],
236
configFiles: [
237
{ name: '.mytoolrc', path: '~' },
238
{ name: 'mytoolfile', path: '.', findUp: true }
239
]
240
});
241
242
// Comprehensive event monitoring
243
let preloadCount = 0;
244
let loaderCount = 0;
245
let respawnOccurred = false;
246
247
MyTool.on('preload:before', function(moduleName) {
248
process.stdout.write(`β³ Loading ${moduleName}...`);
249
});
250
251
MyTool.on('preload:success', function(moduleName, module) {
252
preloadCount++;
253
console.log(` β Loaded ${moduleName}`);
254
255
// Module-specific post-load configuration
256
if (moduleName === 'babel-register') {
257
// Configure Babel after successful load
258
if (module.setDefaultOption) {
259
module.setDefaultOption('presets', ['env']);
260
console.log(' π Configured Babel with env preset');
261
}
262
} else if (moduleName === 'coffee-script/register') {
263
console.log(' β CoffeeScript support enabled');
264
}
265
});
266
267
MyTool.on('preload:failure', function(moduleName, error) {
268
console.log(` β Failed to load ${moduleName}: ${error.message}`);
269
270
// Provide helpful installation hints
271
if (moduleName.includes('coffee-script')) {
272
console.log(' π‘ Try: npm install coffee-script');
273
} else if (moduleName.includes('babel')) {
274
console.log(' π‘ Try: npm install babel-register babel-preset-env');
275
} else if (moduleName.includes('ts-node')) {
276
console.log(' π‘ Try: npm install ts-node typescript');
277
}
278
});
279
280
MyTool.on('loader:success', function(loaderName, module) {
281
loaderCount++;
282
console.log(`π§ Registered loader: ${loaderName}`);
283
});
284
285
MyTool.on('loader:failure', function(loaderName, error) {
286
console.warn(`β οΈ Failed to register loader: ${loaderName}`);
287
console.warn(` Reason: ${error.message}`);
288
});
289
290
MyTool.on('respawn', function(flags, childProcess) {
291
respawnOccurred = true;
292
console.log(`π Respawned with flags: ${flags.join(' ')}`);
293
console.log(` Parent PID: ${process.pid} β Child PID: ${childProcess.pid}`);
294
});
295
296
// Execute the tool with full event monitoring
297
console.log('π Starting MyTool...\n');
298
299
MyTool.prepare({
300
preload: ['babel-register', 'source-map-support/register']
301
}, function(env) {
302
console.log('\nπ Environment Summary:');
303
console.log(` Working Directory: ${env.cwd}`);
304
console.log(` Config File: ${env.configPath || 'none found'}`);
305
console.log(` Local Module: ${env.modulePath || 'none found'}`);
306
console.log(` Preloaded Modules: ${preloadCount}`);
307
console.log(` Registered Loaders: ${loaderCount}`);
308
console.log(` Process Respawned: ${respawnOccurred ? 'yes' : 'no'}`);
309
310
MyTool.execute(env, function(env, argv) {
311
console.log('\n⨠MyTool is ready!');
312
console.log('CLI Arguments:', argv);
313
314
// Your tool's main logic here
315
if (env.configPath) {
316
try {
317
const config = require(env.configPath);
318
console.log('π Configuration loaded:', Object.keys(config));
319
} catch (e) {
320
console.error('β Failed to load config:', e.message);
321
}
322
}
323
});
324
});
325
```
326
327
### Error Event Handling
328
329
Best practices for handling errors in event listeners and preventing unhandled exceptions.
330
331
```javascript
332
const MyApp = new Liftoff({ name: 'myapp' });
333
334
// Always handle errors in event listeners
335
MyApp.on('preload:failure', function(moduleName, error) {
336
// Log the error but don't throw - let execution continue
337
console.error(`Module ${moduleName} failed to load:`, error.message);
338
339
// Optionally exit for critical modules
340
if (moduleName === 'critical-module') {
341
console.error('Critical module failed - cannot continue');
342
process.exit(1);
343
}
344
});
345
346
MyApp.on('loader:failure', function(loaderName, error) {
347
// Loader failures are usually non-fatal
348
console.warn(`Optional loader ${loaderName} unavailable:`, error.message);
349
});
350
351
// Set up global error handling for unhandled exceptions
352
process.on('uncaughtException', function(error) {
353
console.error('Uncaught exception:', error.message);
354
console.error('Stack:', error.stack);
355
process.exit(1);
356
});
357
358
process.on('unhandledRejection', function(reason, promise) {
359
console.error('Unhandled promise rejection:', reason);
360
process.exit(1);
361
});
362
```