@heroku-cli/notifications
Version:
display notifications in Heroku CLI commands
90 lines (89 loc) • 4.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.notify = notify;
const node_child_process_1 = require("node:child_process");
const path = require("node:path");
const DEFAULT_TITLE = 'Heroku CLI';
const DEFAULT_ICON = path.join(__dirname, '../assets/heroku.png');
/**
* Escapes a value for use inside an AppleScript double-quoted string literal.
* Backslashes must be escaped before quotes so the added escapes are not re-escaped.
*/
function escapeAppleScript(value) {
return value.replaceAll('\\', '\\\\').replaceAll('"', String.raw `\"`);
}
/**
* Escapes a value for use inside a PowerShell single-quoted string literal,
* where the only special character is the single quote itself (doubled).
*/
function escapePowerShell(value) {
return value.replaceAll("'", "''");
}
function notifyDarwin(n) {
var _a, _b;
const title = escapeAppleScript((_a = n.title) !== null && _a !== void 0 ? _a : DEFAULT_TITLE);
let script = `display notification "${escapeAppleScript((_b = n.message) !== null && _b !== void 0 ? _b : '')}" with title "${title}"`;
if (n.subtitle)
script += ` subtitle "${escapeAppleScript(n.subtitle)}"`;
if (n.sound)
script += ' sound name "default"';
return ['osascript', ['-e', script]];
}
function notifyLinux(n) {
var _a, _b;
const summary = (_a = n.title) !== null && _a !== void 0 ? _a : DEFAULT_TITLE;
const body = [n.subtitle, n.message].filter(Boolean).join('\n');
const args = [];
const icon = (_b = n.icon) !== null && _b !== void 0 ? _b : DEFAULT_ICON;
if (icon)
args.push('--icon', icon);
args.push(summary, body);
return ['notify-send', args];
}
function notifyWindows(n) {
var _a, _b;
const title = escapePowerShell((_a = n.title) !== null && _a !== void 0 ? _a : DEFAULT_TITLE);
const message = escapePowerShell((_b = n.message) !== null && _b !== void 0 ? _b : '');
// Build a WinRT toast from the bundled ToastText02 template via PowerShell.
const script = [
'[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null;',
'[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null;',
'$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02);',
'$texts = $template.GetElementsByTagName("text");',
`$texts.Item(0).AppendChild($template.CreateTextNode('${title}')) | Out-Null;`,
`$texts.Item(1).AppendChild($template.CreateTextNode('${message}')) | Out-Null;`,
'$toast = [Windows.UI.Notifications.ToastNotification]::new($template);',
`[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('${title}').Show($toast);`,
].join(' ');
return ['powershell', ['-NoProfile', '-NonInteractive', '-Command', script]];
}
const dispatchers = {
darwin: notifyDarwin,
linux: notifyLinux,
win32: notifyWindows,
};
/**
* Display a desktop notification using the host OS's native tooling
* (osascript on macOS, notify-send on Linux, PowerShell toast on Windows).
*
* Best-effort and fire-and-forget: any failure (missing tool, unsupported
* platform) is swallowed so callers never see a notification break a command.
*/
function notify(notification = {}, callback) {
if (!notification.force && ['0', 'false'].includes(process.env.HEROKU_NOTIFICATIONS))
return;
const dispatcher = dispatchers[process.platform];
if (!dispatcher) {
callback === null || callback === void 0 ? void 0 : callback(null, '');
return;
}
try {
const [command, args] = dispatcher(notification);
(0, node_child_process_1.execFile)(command, args, error => {
callback === null || callback === void 0 ? void 0 : callback(error, '');
});
}
catch (_a) {
callback === null || callback === void 0 ? void 0 : callback(null, '');
}
}