1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | import execAsync, { ExecAsyncOptions } from 'exec-async';
|
7 | import path from 'path';
|
8 | import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
|
9 | import util from 'util';
|
10 |
|
11 | function 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 |
|
25 | async 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 |
|
36 | async function osascriptSpawnAsync(
|
37 | script: string | string[],
|
38 | opts?: SpawnOptions
|
39 | ): Promise<SpawnResult> {
|
40 | return await spawnAsync('osascript', osascriptArgs(script), opts);
|
41 | }
|
42 |
|
43 | async 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 |
|
52 | async 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 |
|
60 | async 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 |
|
69 | async function openInAppAsync(appName: string, pth: string): Promise<SpawnResult> {
|
70 | let cmd = 'tell app ' + JSON.stringify(appName) + ' to open ' + JSON.stringify(path.resolve(pth));
|
71 |
|
72 | return await osascriptSpawnAsync(cmd);
|
73 | }
|
74 |
|
75 | async 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 |
|
101 | async function chooseEditorAppAsync(preferredEditor?: string): Promise<string | null> {
|
102 | if (preferredEditor) {
|
103 |
|
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 |
|
133 | async function chooseTerminalAppAsync(): Promise<string | null> {
|
134 | return await chooseAppAsync([
|
135 | 'iTerm 3',
|
136 | 'iTerm 2',
|
137 | 'iTerm',
|
138 | 'HyperTerm',
|
139 |
|
140 |
|
141 |
|
142 | 'Terminal',
|
143 | ]);
|
144 | }
|
145 |
|
146 | async 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 |
|
155 | async 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 |
|
165 | 'end tell',
|
166 | 'end tell',
|
167 | 'end tell',
|
168 | ]);
|
169 |
|
170 | }
|
171 |
|
172 | async 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 |
|
192 | async 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 |
|
205 | export {
|
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 | };
|