/**
* Utilities for working with `osascript` which runs AppleScript on Macs
*/
'use strict';
import execAsync, { ExecAsyncOptions } from 'exec-async';
import path from 'path';
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import util from 'util';
function osascriptArgs(script: string | string[]): string[] {
if (!util.isArray(script)) {
script = [script];
}
let args = [];
for (let line of script) {
args.push('-e');
args.push(line);
}
return args;
}
async function osascriptExecAsync(
script: string | string[],
opts?: ExecAsyncOptions
): Promise<string> {
return await execAsync(
'osascript',
osascriptArgs(script),
Object.assign({ stdio: 'inherit' }, opts)
);
}
async function osascriptSpawnAsync(
script: string | string[],
opts?: SpawnOptions
): Promise<SpawnResult> {
return await spawnAsync('osascript', osascriptArgs(script), opts);
}
async function isAppRunningAsync(appName: string): Promise<boolean> {
let zeroMeansNo = (await osascriptExecAsync(
'tell app "System Events" to count processes whose name is ' + JSON.stringify(appName)
)).trim();
return zeroMeansNo !== '0';
}
async function safeIdOfAppAsync(appName: string): Promise<string | null> {
try {
return (await osascriptExecAsync('id of app ' + JSON.stringify(appName))).trim();
} catch (e) {
return null;
}
}
async function openFinderToFolderAsync(dir: string, activate = true): Promise<void> {
await osascriptSpawnAsync([
'tell application "Finder"',
'open POSIX file ' + JSON.stringify(dir),
(activate && 'activate') || '',
'end tell',
]);
}
async function openInAppAsync(appName: string, pth: string): Promise<SpawnResult> {
let cmd = 'tell app ' + JSON.stringify(appName) + ' to open ' + JSON.stringify(path.resolve(pth));
// console.log("cmd=", cmd);
return await osascriptSpawnAsync(cmd);
}
async function chooseAppAsync(listOfAppNames: string[]): Promise<string | null> {
let runningAwaitables = [];
let appIdAwaitables = [];
for (let appName of listOfAppNames) {
runningAwaitables.push(isAppRunningAsync(appName));
appIdAwaitables.push(safeIdOfAppAsync(appName));
}
let running = await Promise.all(runningAwaitables);
let appIds = await Promise.all(appIdAwaitables);
let i;
for (i = 0; i < listOfAppNames.length; i++) {
if (running[i]) {
return listOfAppNames[i];
}
}
for (i = 0; i < listOfAppNames.length; i++) {
if (appIds[i]) {
return listOfAppNames[i];
}
}
return null;
}
async function chooseEditorAppAsync(preferredEditor?: string): Promise<string | null> {
if (preferredEditor) {
// Make sure this editor exists
let appId = await safeIdOfAppAsync(preferredEditor);
if (appId) {
return preferredEditor;
} else {
console.warn(`Your preferred editor (${preferredEditor}) isn't installed on this computer.`);
}
}
let editorsToTry = [
'Visual Studio Code',
'Atom',
'Sublime Text',
'TextMate',
'TextWrangler',
'Visual Studio Code',
'Brackets',
'SubEthaEdit',
'BBEdit',
'Textastic',
'UltraEdit',
'MacVim',
'CodeRunner 2',
'CodeRunner',
'TextEdit',
];
return await chooseAppAsync(editorsToTry);
}
async function chooseTerminalAppAsync(): Promise<string | null> {
return await chooseAppAsync([
'iTerm 3',
'iTerm 2',
'iTerm',
'HyperTerm',
// 'Cathode',
// 'Terminator',
// 'MacTerm',
'Terminal',
]);
}
async function openInEditorAsync(pth: string, preferredEditor?: string): Promise<SpawnResult> {
let appName = await chooseEditorAppAsync(preferredEditor);
if (!appName) {
throw new Error('No editor found.');
}
console.log('Will open in ' + appName + ' -- ' + pth);
return await openInAppAsync(appName, pth);
}
async function openItermToSpecificFolderAsync(dir: string): Promise<SpawnResult> {
return await osascriptSpawnAsync([
'tell application "iTerm"',
'make new terminal',
'tell the first terminal',
'activate current session',
'launch session "Default Session"',
'tell the last session',
'write text "cd ' + util.inspect(dir) + ' && clear"',
// 'write text "clear"',
'end tell',
'end tell',
'end tell',
]);
// exec("osascript -e 'tell application \"iTerm\"' -e 'make new terminal' -e 'tell the first terminal' -e 'activate current session' -e 'launch session \"Default Session\"' -e 'tell the last session' -e 'write text \"cd #{value}\"' -e 'write text \"clear\"' -e 'end tell' -e 'end tell' -e 'end tell' > /dev/null 2>&1")
}
async function openTerminalToSpecificFolderAsync(dir: string, inTab = false): Promise<SpawnResult> {
if (inTab) {
return await osascriptSpawnAsync([
'tell application "terminal"',
'tell application "System Events" to tell process "terminal" to keystroke "t" using command down',
'do script with command "cd ' +
util.inspect(dir) +
' && clear" in selected tab of the front window',
'end tell',
]);
} else {
return await osascriptSpawnAsync([
'tell application "terminal"',
'do script "cd ' + util.inspect(dir) + ' && clear"',
'end tell',
'tell application "terminal" to activate',
]);
}
}
async function openFolderInTerminalAppAsync(dir: string, inTab = false): Promise<SpawnResult> {
let program = await chooseTerminalAppAsync();
switch (program) {
case 'iTerm':
return await openItermToSpecificFolderAsync(dir);
case 'Terminal':
default:
return await openTerminalToSpecificFolderAsync(dir, inTab);
}
}
export {
chooseAppAsync,
chooseEditorAppAsync,
chooseTerminalAppAsync,
osascriptExecAsync as execAsync,
isAppRunningAsync,
openFinderToFolderAsync,
openFolderInTerminalAppAsync,
openInAppAsync,
openInEditorAsync,
openItermToSpecificFolderAsync,
openTerminalToSpecificFolderAsync,
safeIdOfAppAsync,
osascriptSpawnAsync as spawnAsync,
};