UNPKG

6.02 kBPlain TextView Raw
1/**
2 * Utilities for working with `osascript` which runs AppleScript on Macs
3 */
4'use strict';
5
6import execAsync, { ExecAsyncOptions } from 'exec-async';
7import path from 'path';
8import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
9import util from 'util';
10
11function osascriptArgs(script: string | string[]): string[] {
12 if (!util.isArray(script)) {
13 script = [script];
14 }
15
16 let args = [];
17 for (let line of script) {
18 args.push('-e');
19 args.push(line);
20 }
21
22 return args;
23}
24
25async function osascriptExecAsync(
26 script: string | string[],
27 opts?: ExecAsyncOptions
28): Promise<string> {
29 return await execAsync(
30 'osascript',
31 osascriptArgs(script),
32 Object.assign({ stdio: 'inherit' }, opts)
33 );
34}
35
36async function osascriptSpawnAsync(
37 script: string | string[],
38 opts?: SpawnOptions
39): Promise<SpawnResult> {
40 return await spawnAsync('osascript', osascriptArgs(script), opts);
41}
42
43async function isAppRunningAsync(appName: string): Promise<boolean> {
44 let zeroMeansNo = (
45 await osascriptExecAsync(
46 'tell app "System Events" to count processes whose name is ' + JSON.stringify(appName)
47 )
48 ).trim();
49 return zeroMeansNo !== '0';
50}
51
52async function safeIdOfAppAsync(appName: string): Promise<string | null> {
53 try {
54 return (await osascriptExecAsync('id of app ' + JSON.stringify(appName))).trim();
55 } catch (e) {
56 return null;
57 }
58}
59
60async function openFinderToFolderAsync(dir: string, activate = true): Promise<void> {
61 await osascriptSpawnAsync([
62 'tell application "Finder"',
63 'open POSIX file ' + JSON.stringify(dir),
64 (activate && 'activate') || '',
65 'end tell',
66 ]);
67}
68
69async function openInAppAsync(appName: string, pth: string): Promise<SpawnResult> {
70 let cmd = 'tell app ' + JSON.stringify(appName) + ' to open ' + JSON.stringify(path.resolve(pth));
71 // console.log("cmd=", cmd);
72 return await osascriptSpawnAsync(cmd);
73}
74
75async function chooseAppAsync(listOfAppNames: string[]): Promise<string | null> {
76 let runningAwaitables = [];
77 let appIdAwaitables = [];
78 for (let appName of listOfAppNames) {
79 runningAwaitables.push(isAppRunningAsync(appName));
80 appIdAwaitables.push(safeIdOfAppAsync(appName));
81 }
82 let running = await Promise.all(runningAwaitables);
83 let appIds = await Promise.all(appIdAwaitables);
84
85 let i;
86 for (i = 0; i < listOfAppNames.length; i++) {
87 if (running[i]) {
88 return listOfAppNames[i];
89 }
90 }
91
92 for (i = 0; i < listOfAppNames.length; i++) {
93 if (appIds[i]) {
94 return listOfAppNames[i];
95 }
96 }
97
98 return null;
99}
100
101async function chooseEditorAppAsync(preferredEditor?: string): Promise<string | null> {
102 if (preferredEditor) {
103 // Make sure this editor exists
104 let appId = await safeIdOfAppAsync(preferredEditor);
105 if (appId) {
106 return preferredEditor;
107 } else {
108 console.warn(`Your preferred editor (${preferredEditor}) isn't installed on this computer.`);
109 }
110 }
111
112 let editorsToTry = [
113 'Visual Studio Code',
114 'Atom',
115 'Sublime Text',
116 'TextMate',
117 'TextWrangler',
118 'Visual Studio Code',
119 'Brackets',
120 'SubEthaEdit',
121 'BBEdit',
122 'Textastic',
123 'UltraEdit',
124 'MacVim',
125 'CodeRunner 2',
126 'CodeRunner',
127 'TextEdit',
128 ];
129
130 return await chooseAppAsync(editorsToTry);
131}
132
133async function chooseTerminalAppAsync(): Promise<string | null> {
134 return await chooseAppAsync([
135 'iTerm 3',
136 'iTerm 2',
137 'iTerm',
138 'HyperTerm',
139 // 'Cathode',
140 // 'Terminator',
141 // 'MacTerm',
142 'Terminal',
143 ]);
144}
145
146async function openInEditorAsync(pth: string, preferredEditor?: string): Promise<SpawnResult> {
147 let appName = await chooseEditorAppAsync(preferredEditor);
148 if (!appName) {
149 throw new Error('No editor found.');
150 }
151 console.log('Will open in ' + appName + ' -- ' + pth);
152 return await openInAppAsync(appName, pth);
153}
154
155async function openItermToSpecificFolderAsync(dir: string): Promise<SpawnResult> {
156 return await osascriptSpawnAsync([
157 'tell application "iTerm"',
158 'make new terminal',
159 'tell the first terminal',
160 'activate current session',
161 'launch session "Default Session"',
162 'tell the last session',
163 'write text "cd ' + util.inspect(dir) + ' && clear"',
164 // 'write text "clear"',
165 'end tell',
166 'end tell',
167 'end tell',
168 ]);
169 // 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")
170}
171
172async function openTerminalToSpecificFolderAsync(dir: string, inTab = false): Promise<SpawnResult> {
173 if (inTab) {
174 return await osascriptSpawnAsync([
175 'tell application "terminal"',
176 'tell application "System Events" to tell process "terminal" to keystroke "t" using command down',
177 'do script with command "cd ' +
178 util.inspect(dir) +
179 ' && clear" in selected tab of the front window',
180 'end tell',
181 ]);
182 } else {
183 return await osascriptSpawnAsync([
184 'tell application "terminal"',
185 'do script "cd ' + util.inspect(dir) + ' && clear"',
186 'end tell',
187 'tell application "terminal" to activate',
188 ]);
189 }
190}
191
192async function openFolderInTerminalAppAsync(dir: string, inTab = false): Promise<SpawnResult> {
193 let program = await chooseTerminalAppAsync();
194
195 switch (program) {
196 case 'iTerm':
197 return await openItermToSpecificFolderAsync(dir);
198
199 case 'Terminal':
200 default:
201 return await openTerminalToSpecificFolderAsync(dir, inTab);
202 }
203}
204
205export {
206 chooseAppAsync,
207 chooseEditorAppAsync,
208 chooseTerminalAppAsync,
209 osascriptExecAsync as execAsync,
210 isAppRunningAsync,
211 openFinderToFolderAsync,
212 openFolderInTerminalAppAsync,
213 openInAppAsync,
214 openInEditorAsync,
215 openItermToSpecificFolderAsync,
216 openTerminalToSpecificFolderAsync,
217 safeIdOfAppAsync,
218 osascriptSpawnAsync as spawnAsync,
219};