node-cleanup
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -15,6 +15,14 @@ /****************************************************************************** | ||
var nodeCleanup = require('node-cleanup'); | ||
nodeCleanup(cleanupHandler, terminationMessages); | ||
nodeCleanup(cleanupHandler, stderrMessages); | ||
Or to only install stderr messages: | ||
nodeCleanup() may be called multiple times to install multiple cleanup handlers. However, only the termination messages established by the first call get used. | ||
nodeCleanup(stderrMessages); | ||
Or to install the default stderr messages: | ||
nodeCleanup(); | ||
nodeCleanup() may be called multiple times to install multiple cleanup handlers. However, only the most recently installed stderr messages get used. The messages available are ctrl_C and uncaughtException. | ||
The following uninstalls all cleanup handlers and may be called multiple times in succession: | ||
@@ -29,4 +37,6 @@ | ||
var DEFAULT_SIGINT_MSG = '[ctrl-C]'; | ||
var DEFAULT_EXCEPTION_MSG = 'Uncaught exception...'; | ||
var DEFAULT_MESSAGES = { | ||
ctrl_C: '[ctrl-C]', | ||
uncaughtException: 'Uncaught exception...' | ||
}; | ||
@@ -36,3 +46,3 @@ //// CONFIGURATION //////////////////////////////////////////////////////////// | ||
var cleanupHandlers = null; // array of cleanup handlers to call | ||
var exceptionMessage = null; // stderr message for uncaught exceptions | ||
var messages = null; // messages to write to stderr | ||
@@ -46,3 +56,3 @@ var sigintHandler; // POSIX signal handlers | ||
function signalHandler(signal, message) | ||
function signalHandler(signal) | ||
{ | ||
@@ -55,4 +65,4 @@ var exit = true; | ||
if (exit) { | ||
if (message !== '') | ||
process.stderr.write(message + "\n"); | ||
if (signal === 'SIGINT' && messages && messages.ctrl_C !== '') | ||
process.stderr.write(messages.ctrl_C + "\n"); | ||
uninstall(); // don't cleanup again | ||
@@ -66,4 +76,4 @@ // necessary to communicate the signal to the parent process | ||
{ | ||
if (exceptionMessage !== '') | ||
process.stderr.write(exceptionMessage + "\n"); | ||
if (messages && messages.uncaughtException !== '') | ||
process.stderr.write(messages.uncaughtException + "\n"); | ||
process.stderr.write(e.stack + "\n"); | ||
@@ -82,18 +92,29 @@ process.exit(1); // will call exitHandler() for cleanup | ||
function install(cleanupHandler, messages) | ||
function install(cleanupHandler, stderrMessages) | ||
{ | ||
if (cleanupHandler) { | ||
if (typeof cleanupHandler === 'object') { | ||
stderrMessages = cleanupHandler; | ||
cleanupHandler = null; | ||
} | ||
} | ||
else if (!stderrMessages) | ||
stderrMessages = DEFAULT_MESSAGES; | ||
if (stderrMessages) { | ||
if (messages === null) | ||
messages = { ctrl_C: '', uncaughtException: '' }; | ||
if (typeof stderrMessages.ctrl_C === 'string') | ||
messages.ctrl_C = stderrMessages.ctrl_C; | ||
if (typeof stderrMessages.uncaughtException === 'string') | ||
messages.uncaughtException = stderrMessages.uncaughtException; | ||
} | ||
if (cleanupHandlers === null) { | ||
cleanupHandlers = []; // establish before installing handlers | ||
messages = messages || {}; | ||
if (typeof messages.ctrl_C !== 'string') | ||
messages.ctrl_C = DEFAULT_SIGINT_MSG; | ||
if (typeof messages.uncaughtException !== 'string') | ||
messages.uncaughtException = DEFAULT_EXCEPTION_MSG; | ||
exceptionMessage = messages.uncaughtException; | ||
sigintHandler = signalHandler.bind(this, 'SIGINT', messages.ctrl_C); | ||
sighupHandler = signalHandler.bind(this, 'SIGHUP', ''); | ||
sigquitHandler = signalHandler.bind(this, 'SIGQUIT', ''); | ||
sigtermHandler = signalHandler.bind(this, 'SIGTERM', ''); | ||
sigintHandler = signalHandler.bind(this, 'SIGINT'); | ||
sighupHandler = signalHandler.bind(this, 'SIGHUP'); | ||
sigquitHandler = signalHandler.bind(this, 'SIGQUIT'); | ||
sigtermHandler = signalHandler.bind(this, 'SIGTERM'); | ||
@@ -100,0 +121,0 @@ process.on('SIGINT', sigintHandler); |
{ | ||
"name": "node-cleanup", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "installs cleanup handlers that always run on exiting node", | ||
"main": "node-cleanup.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "tap tests/*.js" | ||
}, | ||
@@ -9,0 +9,0 @@ "repository": { |
@@ -45,3 +45,3 @@ # node-cleanup | ||
```js | ||
nodeCleanup(null, { | ||
nodeCleanup({ | ||
ctrl_C: "{^C}", | ||
@@ -52,3 +52,3 @@ uncaughtException: "Uh oh. Look what happened:" | ||
To get just the default `stderr` messages, without installing a cleanup handler: | ||
To get the default `stderr` messages, without installing a cleanup handler: | ||
@@ -109,15 +109,18 @@ ```js | ||
`nodeCleanup()` has the following ([FlowType](https://flowtype.org/docs/getting-started.html#_)) signature: | ||
`nodeCleanup()` has the following available ([FlowType](https://flowtype.org/docs/getting-started.html#_)) signatures: | ||
```js | ||
function nodeCleanup(cleanupHandler?: Function, messages?: object): void | ||
function nodeCleanup(cleanupHandler: Function): void | ||
function nodeCleanup(cleanupHandler: Function, stderrMessages: object): void | ||
function nodeCleanup(stderrMessages: object): void | ||
function nodeCleanup(): void | ||
``` | ||
`nodeCleanup()` installs a cleanup handler. It may also assign messages to write to `stderr` on SIGINT or an uncaught exception. Both parameters are optional. If not `cleanupHandler` is provided, the `stderr` messages are still written. If no `messages` are provided, default `stderr` messages are written. Calling `nodeCleanup()` with no parameters just installs these default messages. | ||
The 1st form installs a cleanup handler. The 2nd form also assigns messages to write to `stderr` on SIGINT or an uncaught exception. The 3rd and 4th forms only assign messages to write to `stderr`, without installing a cleanup handler. The 4th form assigns default `stderr` messages. | ||
`cleanupHandler` is a cleanup handler callback and is described in its own section below. When null or undefined, termination events all result in the process terminating, including signals. | ||
`cleanupHandler` is a cleanup handler callback and is described in its own section below. When no cleanup handlers are installed, termination events all result in the process terminating, including signal events. | ||
`messages` is an object mapping any of the keys `ctrl_C` and `uncaughtException` to message strings that output to `stderr`. Default messages are provided for omitted messages. Set a message to the empty string `''` inhibit the message. | ||
`stderrMessages` is an object mapping any of the keys `ctrl_C` and `uncaughtException` to message strings that output to `stderr`. Set a message to the empty string `''` inhibit a previously-assigned message. | ||
`nodeCleanup()` may be called multiple times to install multiple cleanup handlers. Each of these handlers runs for each signal or termination condition. The first call to `nodeCleanup()` establishes the `stderr` messages; messages passed to subsequent calls are ignored. | ||
`nodeCleanup()` may be called multiple times to install multiple cleanup handlers or override previous messages. Each handler gets called on each signal or termination condition. The most recently assigned messages apply. | ||
@@ -166,5 +169,11 @@ ### `nodeCleanup.uninstall()` | ||
## Incompatibilities with v1.0.x | ||
`node-cleanup` v2+ is not fully compatible with v1.x. You may need to change your usage to upgrade. These are the potential incompatibilities: | ||
- The cleanup handlers now also run on SIGHUP, SIGQUIT, and SIGTERM, which were not getting cleanup processing before. | ||
- `stderr` messages are handled quite differently. Previously, there were defaults that you had to override, and only your first message assignments applied. Now, the defaults **only** install with the parameterless call `nodeCleanup()`. Otherwise there are no messages unless you provide them. Moreover, the most recent message assignments are the ones that get used. | ||
## Credit | ||
This module began by borrowing and modifying code from CanyonCasa's [answer to a stackoverflow question](http://stackoverflow.com/a/21947851/650894). I had found the code necessary for all my node projects. @Banjocat piped in with a [comment](http://stackoverflow.com/questions/14031763/doing-a-cleanup-action-just-before-node-js-exits/21947851#comment68567869_21947851) about how the solution didn't properly handle SIGINT. (See [this detailed explanation](https://www.cons.org/cracauer/sigint.html) of the SIGINT problem). I have completely rewritten the module to properly deal with SIGINT and other signals (I hope!). The rewrite also provides some additional flexibility I found myself needing in my projects. | ||
This module began by borrowing and modifying code from CanyonCasa's [answer to a stackoverflow question](http://stackoverflow.com/a/21947851/650894). I had found the code necessary for all my node projects. @Banjocat piped in with a [comment](http://stackoverflow.com/questions/14031763/doing-a-cleanup-action-just-before-node-js-exits/21947851#comment68567869_21947851) about how the solution didn't properly handle SIGINT. (See [this detailed explanation](https://www.cons.org/cracauer/sigint.html) of the SIGINT problem). I have completely rewritten the module to properly deal with SIGINT and other signals (I hope!). The rewrite also provides some additional flexibility that @zixia and I found ourselves needing for our respective projects. |
@@ -8,2 +8,3 @@ #!/usr/bin/env node | ||
handlers; number; // 0, 1, or 2 concurrent cleanup handlers | ||
messages0: object|null; // messages argument for no-cleanup call, if any | ||
messages1: object|null; // messages argument for 1st nodeCleanup() call | ||
@@ -51,4 +52,8 @@ messages2: object|null; // messages argument for 2nd nodeCleanup() call | ||
if (config.handlers === 0) | ||
nodeCleanup(); | ||
if (config.handlers === 0) { | ||
if (config.messages0) | ||
nodeCleanup(config.messages0); | ||
else | ||
nodeCleanup(); | ||
} | ||
else { | ||
@@ -55,0 +60,0 @@ nodeCleanup(cleanup1, config.messages1); |
@@ -25,30 +25,11 @@ // tests in which spawned child installs multiple cleanup handlers | ||
t.test("multiple handlers: uncaught exception - default message", function (t) { | ||
t.test("multiple handlers: uncaught exception - custom messages", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: null, | ||
messages2: null, | ||
return1: true, | ||
return2: true, | ||
exception: true, | ||
uninstall: false | ||
}, function (childPID) { | ||
// no signal | ||
}, { | ||
exitReason: 1, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: lib.DEFAULT_EXCEPTION_OUT | ||
}); | ||
}); | ||
t.test("multiple handlers: uncaught exception - custom message", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
uncaughtException: "Look! A surprise!" | ||
uncaughtException: "Not the surprise you're looking for." | ||
}, | ||
messages2: { | ||
uncaughtException: "Not the surprise you're looking for." | ||
uncaughtException: "Look! A surprise!" | ||
}, | ||
@@ -68,3 +49,53 @@ return1: true, | ||
t.test("multiple handlers: child SIGINT - both heeded", function (t) { | ||
t.test("multiple handlers: uncaught exception - removed message", | ||
function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
uncaughtException: "Not the surprise you're looking for." | ||
}, | ||
messages2: { | ||
uncaughtException: "" | ||
}, | ||
return1: true, | ||
return2: true, | ||
exception: true, | ||
uninstall: false | ||
}, function (childPID) { | ||
// no signal | ||
}, { | ||
exitReason: 1, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: /tests[\/\\]bin[\/\\]stackable.js/ | ||
}); | ||
} | ||
); | ||
t.test("multiple handlers: uncaught exception - added message", | ||
function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
ctrl_C: "{^C}}" | ||
}, | ||
messages2: { | ||
uncaughtException: "Oops!" | ||
}, | ||
return1: true, | ||
return2: true, | ||
exception: true, | ||
uninstall: false | ||
}, function (childPID) { | ||
// no signal | ||
}, { | ||
exitReason: 1, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: /^Oops!/ | ||
}); | ||
} | ||
); | ||
t.test("multiple handlers: uncaught exception - no message", function (t) { | ||
lib.test(t, { | ||
@@ -77,39 +108,99 @@ child: 'stackable', | ||
return2: true, | ||
exception: false, | ||
exception: true, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
// no signal | ||
}, { | ||
exitReason: 'SIGINT', | ||
exitReason: 1, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: lib.DEFAULT_SIGINT_OUT | ||
stderr: /tests[\/\\]bin[\/\\]stackable.js/ | ||
}); | ||
}); | ||
t.test("multiple handlers: child SIGINT - first heeded", function (t) { | ||
t.test("multiple handlers: child SIGINT - both heeded, custom messages", | ||
function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
}, | ||
messages2: { | ||
ctrl_C: "{^C2}" | ||
}, | ||
return1: true, | ||
return2: true, | ||
exception: false, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 'SIGINT', | ||
stdout: "cleanup1 cleanup2", | ||
stderr: "{^C2}\n" | ||
}); | ||
} | ||
); | ||
t.test("multiple handlers: child SIGINT - first heeded, custom messages", | ||
function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
}, | ||
messages2: { | ||
ctrl_C: "{^C2}" | ||
}, | ||
return1: true, | ||
return2: false, | ||
exception: false, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 0, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: "" | ||
}); | ||
} | ||
); | ||
t.test("multiple handlers: child SIGINT - second heeded, custom messages", | ||
function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
}, | ||
messages2: { | ||
ctrl_C: "{^C2}" | ||
}, | ||
return1: false, | ||
return2: true, | ||
exception: false, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 0, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: "" | ||
}); | ||
} | ||
); | ||
t.test("multiple handlers: child SIGINT - removed message", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: null, | ||
messages2: null, | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
}, | ||
messages2: { | ||
ctrl_C: "" | ||
}, | ||
return1: true, | ||
return2: false, | ||
exception: false, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 0, | ||
stdout: "cleanup1 cleanup2", | ||
stderr: "" | ||
}); | ||
}); | ||
t.test("multiple handlers: child SIGINT - second heeded", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 2, | ||
messages1: null, | ||
messages2: null, | ||
return1: false, | ||
return2: true, | ||
@@ -121,3 +212,3 @@ exception: false, | ||
}, { | ||
exitReason: 0, | ||
exitReason: 'SIGINT', | ||
stdout: "cleanup1 cleanup2", | ||
@@ -128,3 +219,3 @@ stderr: "" | ||
t.test("multiple handlers: child SIGINT - custom message", function (t) { | ||
t.test("multiple handlers: child SIGINT - added message", function (t) { | ||
lib.test(t, { | ||
@@ -134,6 +225,6 @@ child: 'stackable', | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
uncaughtException: "Oops!" | ||
}, | ||
messages2: { | ||
ctrl_C: "{^C2}" | ||
ctrl_C: "{^C1}" | ||
}, | ||
@@ -233,4 +324,8 @@ return1: true, | ||
handlers: 2, | ||
messages1: null, | ||
messages2: null, | ||
messages1: { | ||
uncaughtException: "Shouldn't show." | ||
}, | ||
messages2: { | ||
uncaughtException: "Also shouldn't show." | ||
}, | ||
return1: true, | ||
@@ -253,4 +348,8 @@ return2: true, | ||
handlers: 2, | ||
messages1: null, | ||
messages2: null, | ||
messages1: { | ||
ctrl_C: "{^C1}" | ||
}, | ||
messages2: { | ||
ctrl_C: "{^C2}" | ||
}, | ||
return1: true, | ||
@@ -257,0 +356,0 @@ return2: true, |
@@ -21,3 +21,3 @@ // tests in which spawned child uses the default cleanup handler | ||
t.test("nocleanup: uncaught exception", function (t) { | ||
t.test("nocleanup: uncaught exception - default message", function (t) { | ||
lib.test(t, { | ||
@@ -37,6 +37,24 @@ child: 'stackable', | ||
t.test("nocleanup: child SIGINT", function (t) { | ||
t.test("nocleanup: uncaught exception - custom message", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 0, | ||
messages0: { | ||
uncaughtException: "Yikes!" | ||
}, | ||
exception: true, | ||
uninstall: false | ||
}, function (childPID) { | ||
// no signal | ||
}, { | ||
exitReason: 1, | ||
stdout: "", | ||
stderr: /^Yikes!/ | ||
}); | ||
}); | ||
t.test("nocleanup: child SIGINT - default message", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 0, | ||
exception: false, | ||
@@ -53,2 +71,20 @@ uninstall: false | ||
t.test("nocleanup: child SIGINT - custom message", function (t) { | ||
lib.test(t, { | ||
child: 'stackable', | ||
handlers: 0, | ||
messages0: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
uninstall: false | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 'SIGINT', | ||
stdout: "", | ||
stderr: "{^C}\n" | ||
}); | ||
}); | ||
t.test("nocleanup: child SIGQUIT", function (t) { | ||
@@ -55,0 +91,0 @@ lib.test(t, { |
@@ -60,20 +60,2 @@ // tests in which spawned child installs a custom cleanup handler | ||
t.test("single: uncaught exception - default message", function (t) { | ||
lib.test(t, { | ||
child: 'groupable', | ||
grandchild: false, | ||
grandchildHeedsSIGINT: false, | ||
messages: null, | ||
exception: true, | ||
skipTermination: false, | ||
exitReturn: 'true' | ||
}, function (childPID) { | ||
// no signal | ||
}, { | ||
exitReason: 1, | ||
stdout: "cleanup", | ||
stderr: lib.DEFAULT_EXCEPTION_OUT | ||
}); | ||
}); | ||
t.test("single: uncaught exception - custom message", function (t) { | ||
@@ -119,3 +101,3 @@ lib.test(t, { | ||
t.test("single: child SIGINT - true return, default message", function (t) { | ||
t.test("single: child SIGINT - true return, custom message", function (t) { | ||
lib.test(t, { | ||
@@ -125,3 +107,5 @@ child: 'groupable', | ||
grandchildHeedsSIGINT: false, | ||
messages: null, | ||
messages: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
@@ -135,7 +119,7 @@ skipTermination: false, | ||
stdout: "cleanup", | ||
stderr: lib.DEFAULT_SIGINT_OUT | ||
stderr: "{^C}\n" | ||
}); | ||
}); | ||
t.test("single: child SIGINT - undefined return, default message", | ||
t.test("single: child SIGINT - undefined return, custom message", | ||
function (t) { | ||
@@ -146,3 +130,5 @@ lib.test(t, { | ||
grandchildHeedsSIGINT: false, | ||
messages: null, | ||
messages: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
@@ -156,3 +142,3 @@ skipTermination: false, | ||
stdout: "cleanup", | ||
stderr: lib.DEFAULT_SIGINT_OUT | ||
stderr: "{^C}\n" | ||
}); | ||
@@ -162,22 +148,2 @@ } | ||
t.test("single: child SIGINT - custom message", function (t) { | ||
lib.test(t, { | ||
child: 'groupable', | ||
grandchild: false, | ||
grandchildHeedsSIGINT: false, | ||
messages: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
skipTermination: false, | ||
exitReturn: 'true' | ||
}, function (childPID) { | ||
process.kill(childPID, 'SIGINT'); | ||
}, { | ||
exitReason: 'SIGINT', | ||
stdout: "cleanup", | ||
stderr: "{^C}\n" | ||
}); | ||
}); | ||
t.test("single: child SIGINT - no message", function (t) { | ||
@@ -188,5 +154,3 @@ lib.test(t, { | ||
grandchildHeedsSIGINT: false, | ||
messages: { | ||
ctrl_C: "" | ||
}, | ||
messages: null, | ||
exception: false, | ||
@@ -209,3 +173,5 @@ skipTermination: false, | ||
grandchildHeedsSIGINT: false, | ||
messages: null, | ||
messages: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
@@ -228,3 +194,5 @@ skipTermination: false, | ||
grandchildHeedsSIGINT: true, | ||
messages: null, | ||
messages: { | ||
ctrl_C: "{^C}" | ||
}, | ||
exception: false, | ||
@@ -238,3 +206,3 @@ skipTermination: false, | ||
stdout: "skipped_cleanup grandchild=SIGINT cleanup", | ||
stderr: lib.DEFAULT_SIGINT_OUT | ||
stderr: "{^C}\n" | ||
}); | ||
@@ -241,0 +209,0 @@ }); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
51809
1223
0
176
2