UNPKG

@heroku-cli/notifications

Version:
90 lines (89 loc) 4.07 kB
"use strict"; 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, ''); } }