From 2d24b59c4f1182b9908b717f7dfacff8cc0bdc51 Mon Sep 17 00:00:00 2001 From: Jason Gore Date: Wed, 17 Jun 2020 09:44:21 -0700 Subject: [PATCH 1/2] Fix named pipe server hang when notifications are disabled in Windows 10. --- lib/utils.js | 76 ++++++++++++++++++++++---------------------- notifiers/toaster.js | 24 +++++++++----- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index f7e05d2..3a577f6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -16,7 +16,7 @@ function clone(obj) { module.exports.clone = clone; -var escapeQuotes = function(str) { +var escapeQuotes = function (str) { if (typeof str === 'string') { return str.replace(/(["$`\\])/g, '\\$1'); } else { @@ -24,7 +24,7 @@ var escapeQuotes = function(str) { } }; -var inArray = function(arr, val) { +var inArray = function (arr, val) { return arr.indexOf(val) !== -1; }; @@ -45,10 +45,10 @@ var notifySendFlags = { h: 'hint', hint: 'hint', a: 'app-name', - 'app-name': 'app-name' + 'app-name': 'app-name', }; -module.exports.command = function(notifier, options, cb) { +module.exports.command = function (notifier, options, cb) { notifier = shellwords.escape(notifier); if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (command):'); @@ -56,7 +56,7 @@ module.exports.command = function(notifier, options, cb) { console.info('[notifier options]', options.join(' ')); } - return cp.exec(notifier + ' ' + options.join(' '), function( + return cp.exec(notifier + ' ' + options.join(' '), function ( error, stdout, stderr @@ -66,26 +66,26 @@ module.exports.command = function(notifier, options, cb) { }); }; -module.exports.fileCommand = function(notifier, options, cb) { +module.exports.fileCommand = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (fileCommand):'); console.info('[notifier path]', notifier); console.info('[notifier options]', options.join(' ')); } - return cp.execFile(notifier, options, function(error, stdout, stderr) { + return cp.execFile(notifier, options, function (error, stdout, stderr) { if (error) return cb(error, stdout); cb(stderr, stdout); }); }; -module.exports.fileCommandJson = function(notifier, options, cb) { +module.exports.fileCommandJson = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (fileCommandJson):'); console.info('[notifier path]', notifier); console.info('[notifier options]', options.join(' ')); } - return cp.execFile(notifier, options, function(error, stdout, stderr) { + return cp.execFile(notifier, options, function (error, stdout, stderr) { if (error) return cb(error, stdout); if (!stdout) return cb(error, {}); @@ -98,13 +98,13 @@ module.exports.fileCommandJson = function(notifier, options, cb) { }); }; -module.exports.immediateFileCommand = function(notifier, options, cb) { +module.exports.immediateFileCommand = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (notifier):'); console.info('[notifier path]', notifier); } - notifierExists(notifier, function(_, exists) { + notifierExists(notifier, function (_, exists) { if (!exists) { return cb(new Error('Notifier (' + notifier + ') not found on system.')); } @@ -114,7 +114,7 @@ module.exports.immediateFileCommand = function(notifier, options, cb) { }; function notifierExists(notifier, cb) { - return fs.stat(notifier, function(err, stat) { + return fs.stat(notifier, function (err, stat) { if (!err) return cb(err, stat.isFile()); // Check if Windows alias @@ -124,14 +124,14 @@ function notifierExists(notifier, cb) { } // Check if there is an exe file in the directory - return fs.stat(notifier + '.exe', function(err, stat) { + return fs.stat(notifier + '.exe', function (err, stat) { if (err) return cb(err, false); cb(err, stat.isFile()); }); }); } -var mapAppIcon = function(options) { +var mapAppIcon = function (options) { if (options.appIcon) { options.icon = options.appIcon; delete options.appIcon; @@ -140,7 +140,7 @@ var mapAppIcon = function(options) { return options; }; -var mapText = function(options) { +var mapText = function (options) { if (options.text) { options.message = options.text; delete options.text; @@ -149,7 +149,7 @@ var mapText = function(options) { return options; }; -var mapIconShorthand = function(options) { +var mapIconShorthand = function (options) { if (options.i) { options.icon = options.i; delete options.i; @@ -158,7 +158,7 @@ var mapIconShorthand = function(options) { return options; }; -module.exports.mapToNotifySend = function(options) { +module.exports.mapToNotifySend = function (options) { options = mapAppIcon(options); options = mapText(options); @@ -173,7 +173,7 @@ module.exports.mapToNotifySend = function(options) { return options; }; -module.exports.mapToGrowl = function(options) { +module.exports.mapToGrowl = function (options) { options = mapAppIcon(options); options = mapIconShorthand(options); options = mapText(options); @@ -187,7 +187,7 @@ module.exports.mapToGrowl = function(options) { return options; }; -module.exports.mapToMac = function(options) { +module.exports.mapToMac = function (options) { options = mapIconShorthand(options); options = mapText(options); @@ -233,7 +233,7 @@ function isArray(arr) { module.exports.isArray = isArray; function noop() {} -module.exports.actionJackerDecorator = function(emitter, options, fn, mapper) { +module.exports.actionJackerDecorator = function (emitter, options, fn, mapper) { options = clone(options); fn = fn || noop; @@ -244,7 +244,7 @@ module.exports.actionJackerDecorator = function(emitter, options, fn, mapper) { ); } - return function(err, data) { + return function (err, data) { var resultantData = data; var metadata = {}; // Allow for extra data if resultantData is an object @@ -273,7 +273,7 @@ module.exports.actionJackerDecorator = function(emitter, options, fn, mapper) { }; }; -module.exports.constructArgumentList = function(options, extra) { +module.exports.constructArgumentList = function (options, extra) { var args = []; extra = extra || {}; @@ -287,7 +287,7 @@ module.exports.constructArgumentList = function(options, extra) { var keepNewlines = !!extra.keepNewlines; var wrapper = extra.wrapper === undefined ? '"' : extra.wrapper; - var escapeFn = function(arg) { + var escapeFn = function (arg) { if (isArray(arg)) { return removeNewLines(arg.join(',')); } @@ -301,7 +301,7 @@ module.exports.constructArgumentList = function(options, extra) { return wrapper + arg + wrapper; }; - initial.forEach(function(val) { + initial.forEach(function (val) { args.push(escapeFn(val)); }); for (var key in options) { @@ -352,11 +352,11 @@ var allowedToasterFlags = [ 'pid', 'pipeName', 'close', - 'install' + 'install', ]; var toasterSoundPrefix = 'Notification.'; var toasterDefaultSound = 'Notification.Default'; -module.exports.mapToWin8 = function(options) { +module.exports.mapToWin8 = function (options) { options = mapAppIcon(options); options = mapText(options); @@ -439,7 +439,7 @@ module.exports.mapToWin8 = function(options) { return options; }; -module.exports.mapToNotifu = function(options) { +module.exports.mapToNotifu = function (options) { options = mapAppIcon(options); options = mapText(options); @@ -492,29 +492,29 @@ module.exports.mapToNotifu = function(options) { return options; }; -module.exports.isMac = function() { +module.exports.isMac = function () { return os.type() === 'Darwin'; }; -module.exports.isMountainLion = function() { +module.exports.isMountainLion = function () { return ( os.type() === 'Darwin' && semver.satisfies(garanteeSemverFormat(os.release()), '>=12.0.0') ); }; -module.exports.isWin8 = function() { +module.exports.isWin8 = function () { return ( os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '>=6.2.9200') ); }; -module.exports.isWSL = function() { +module.exports.isWSL = function () { return isWSL; }; -module.exports.isLessThanWin8 = function() { +module.exports.isLessThanWin8 = function () { return ( os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '<6.2.9200') @@ -538,19 +538,19 @@ function sanitizeNotifuTypeArgument(type) { return 'info'; } -module.exports.createNamedPipe = namedPipe => { +module.exports.createNamedPipe = (server) => { const buf = Buffer.alloc(BUFFER_SIZE); - return new Promise(resolve => { - const server = net.createServer(stream => { - stream.on('data', c => { + return new Promise((resolve) => { + server.instance = net.createServer((stream) => { + stream.on('data', (c) => { buf.write(c.toString()); }); stream.on('end', () => { - server.close(); + server.instance.close(); }); }); - server.listen(namedPipe, () => { + server.instance.listen(server.namedPipe, () => { resolve(buf); }); }); diff --git a/notifiers/toaster.js b/notifiers/toaster.js index 77b0139..a5d0ff6 100644 --- a/notifiers/toaster.js +++ b/notifiers/toaster.js @@ -54,7 +54,9 @@ function notifyRaw(options, callback) { callback = callback || noop; var is64Bit = os.arch() === 'x64'; var resultBuffer; - const namedPipe = getPipeName(); + const server = { + namedPipe: getPipeName(), + }; if (typeof options === 'string') { options = { title: 'node-notifier', message: options }; @@ -94,16 +96,22 @@ function notifyRaw(options, callback) { callback(err, result); } callback(null, result); + + // https://github.com/mikaelbr/node-notifier/issues/334 + // Due to an issue with snoretoast not using stdio and pipe + // when notifications are disabled, make sure named pipe server + // is closed before exiting. + server.instance && server.instance.close(); }; - var actionJackedCallback = err => + var actionJackedCallback = (err) => snoreToastResultParser( err, utils.actionJackerDecorator( this, options, callback, - data => data || false + (data) => data || false ) ); @@ -122,16 +130,16 @@ function notifyRaw(options, callback) { } // Add pipeName option, to get the output - utils.createNamedPipe(namedPipe).then(out => { + utils.createNamedPipe(server).then((out) => { resultBuffer = out; - options.pipeName = namedPipe; + options.pipeName = server.namedPipe; options = utils.mapToWin8(options); var argsList = utils.constructArgumentList(options, { explicitTrue: true, wrapper: '', keepNewlines: true, - noEscape: true + noEscape: true, }); var notifierWithArch = notifier + '-x' + (is64Bit ? '64' : '86') + '.exe'; @@ -145,8 +153,8 @@ function notifyRaw(options, callback) { } Object.defineProperty(WindowsToaster.prototype, 'notify', { - get: function() { + get: function () { if (!this._notify) this._notify = notifyRaw.bind(this); return this._notify; - } + }, }); From 9beb28586d965dd7c06f475e2c8db6d9864d9322 Mon Sep 17 00:00:00 2001 From: Jason Gore Date: Wed, 17 Jun 2020 10:03:38 -0700 Subject: [PATCH 2/2] Fix lint rules. --- lib/utils.js | 4 ++-- notifiers/toaster.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 3a577f6..7ab0c78 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -45,7 +45,7 @@ var notifySendFlags = { h: 'hint', hint: 'hint', a: 'app-name', - 'app-name': 'app-name', + 'app-name': 'app-name' }; module.exports.command = function (notifier, options, cb) { @@ -352,7 +352,7 @@ var allowedToasterFlags = [ 'pid', 'pipeName', 'close', - 'install', + 'install' ]; var toasterSoundPrefix = 'Notification.'; var toasterDefaultSound = 'Notification.Default'; diff --git a/notifiers/toaster.js b/notifiers/toaster.js index a5d0ff6..0bbb873 100644 --- a/notifiers/toaster.js +++ b/notifiers/toaster.js @@ -55,7 +55,7 @@ function notifyRaw(options, callback) { var is64Bit = os.arch() === 'x64'; var resultBuffer; const server = { - namedPipe: getPipeName(), + namedPipe: getPipeName() }; if (typeof options === 'string') { @@ -139,7 +139,7 @@ function notifyRaw(options, callback) { explicitTrue: true, wrapper: '', keepNewlines: true, - noEscape: true, + noEscape: true }); var notifierWithArch = notifier + '-x' + (is64Bit ? '64' : '86') + '.exe'; @@ -156,5 +156,5 @@ Object.defineProperty(WindowsToaster.prototype, 'notify', { get: function () { if (!this._notify) this._notify = notifyRaw.bind(this); return this._notify; - }, + } });