import { logger } from '@services/libs/log'; import { pathExists } from 'fs-extra'; import appPath from './app-path'; import type { IFoundEditor } from './found-editor'; /** Represents an external editor on macOS */ interface IDarwinExternalEditor { /** * List of bundle identifiers that are used by the app in its multiple * versions. */ readonly bundleIdentifiers: string[]; /** Name of the editor. It will be used both as identifier and user-facing. */ readonly name: string; } /** * This list contains all the external editors supported on macOS. Add a new * entry here to add support for your favorite editor. */ const editors: IDarwinExternalEditor[] = [ { name: 'Atom', bundleIdentifiers: ['com.github.atom'], }, { name: 'MacVim', bundleIdentifiers: ['org.vim.MacVim'], }, { name: 'Visual Studio Code', bundleIdentifiers: ['com.microsoft.VSCode'], }, { name: 'Visual Studio Code (Insiders)', bundleIdentifiers: ['com.microsoft.VSCodeInsiders'], }, { name: 'VSCodium', bundleIdentifiers: ['com.visualstudio.code.oss'], }, { name: 'Sublime Text', bundleIdentifiers: ['com.sublimetext.4', 'com.sublimetext.3', 'com.sublimetext.2'], }, { name: 'BBEdit', bundleIdentifiers: ['com.barebones.bbedit'], }, { name: 'PhpStorm', bundleIdentifiers: ['com.jetbrains.PhpStorm'], }, { name: 'PyCharm', bundleIdentifiers: ['com.jetbrains.PyCharm'], }, { name: 'RubyMine', bundleIdentifiers: ['com.jetbrains.RubyMine'], }, { name: 'RStudio', bundleIdentifiers: ['org.rstudio.RStudio'], }, { name: 'TextMate', bundleIdentifiers: ['com.macromates.TextMate'], }, { name: 'Brackets', bundleIdentifiers: ['io.brackets.appshell'], }, { name: 'WebStorm', bundleIdentifiers: ['com.jetbrains.WebStorm'], }, { name: 'Typora', bundleIdentifiers: ['abnerworks.Typora'], }, { name: 'CodeRunner', bundleIdentifiers: ['com.krill.CodeRunner'], }, { name: 'SlickEdit', bundleIdentifiers: ['com.slickedit.SlickEditPro2018', 'com.slickedit.SlickEditPro2017', 'com.slickedit.SlickEditPro2016', 'com.slickedit.SlickEditPro2015'], }, { name: 'IntelliJ', bundleIdentifiers: ['com.jetbrains.intellij'], }, { name: 'IntelliJ Community Edition', bundleIdentifiers: ['com.jetbrains.intellij.ce'], }, { name: 'Xcode', bundleIdentifiers: ['com.apple.dt.Xcode'], }, { name: 'GoLand', bundleIdentifiers: ['com.jetbrains.goland'], }, { name: 'Android Studio', bundleIdentifiers: ['com.google.android.studio'], }, { name: 'Rider', bundleIdentifiers: ['com.jetbrains.rider'], }, { name: 'Nova', bundleIdentifiers: ['com.panic.Nova'], }, ]; /** * This list contains all the external git GUI app supported on macOS. Add a new * entry here to add support for your favorite git GUI app. */ const gitGUIApp: IDarwinExternalEditor[] = [ { name: 'GitHub Desktop', bundleIdentifiers: ['com.github.GitHubClient'], }, { name: 'GitKraken', bundleIdentifiers: ['com.axosoft.gitkraken'], }, { name: 'SourceTree', bundleIdentifiers: ['com.torusknot.SourceTreeNotMAS'], }, { name: 'Tower', bundleIdentifiers: ['com.fournova.Tower2', 'com.fournova.Tower3'], }, ]; async function findApplication(editor: IDarwinExternalEditor): Promise { for (const identifier of editor.bundleIdentifiers) { try { logger.info('Trying to get app path', { identifier, function: 'darwin.findApplication', }); // app-path not finding the app isn't an error, it just means the // bundle isn't registered on the machine. // https://github.com/sindresorhus/app-path/blob/0e776d4e132676976b4a64e09b5e5a4c6e99fcba/index.js#L7-L13 const installPath = await appPath(identifier).catch(async (error_: unknown) => { const error = error_ as Error; logger.info('gets appPath Error', { error, function: 'darwin.findApplication' }); if (error.message === "Couldn't find the app") { return await Promise.resolve(null); } return await Promise.reject(error); }); logger.info('Found path for identifier', { identifier, installPath: String(installPath), function: 'darwin.findApplication', }); if (installPath === null) { return null; } if (await pathExists(installPath)) { return installPath; } logger.info('App installation not found at path', { editorName: editor.name, installPath, function: 'darwin.findApplication', }); } catch (error_: unknown) { const error = error_ as Error; logger.info('unable to locate installation', { editorName: editor.name, error, function: 'darwin.findApplication' }); } } return null; } /** * Lookup known external editors using the bundle ID that each uses * to register itself on a user's machine when installing. */ export async function getAvailableEditors(editorName?: string): Promise>> { const results: Array> = []; for (const editor of editors.filter((editor) => editorName === undefined || editor.name === editorName)) { const path = await findApplication(editor); if (path !== null) { results.push({ editor: editor.name, path }); } } return results; } /** * Lookup known external git GUI app using the bundle ID that each uses * to register itself on a user's machine when installing. */ export async function getAvailableGitGUIApps(): Promise>> { const results: Array> = []; for (const guiApp of gitGUIApp) { const path = await findApplication(guiApp); if (path) { results.push({ editor: guiApp.name, path }); } } return results; }