import * as path from 'path' import * as fs from 'mz/fs' import { exec } from 'mz/child_process' import axios from 'axios' import { Observable } from 'rxjs' import { Injectable } from '@angular/core' import { Logger, LogService, ConfigService, HostAppService, Platform } from 'terminus-core' const NAME_PREFIX = 'terminus-' const KEYWORD = 'terminus-plugin' const OFFICIAL_NPM_ACCOUNT = 'eugenepankov' export interface IPluginInfo { name: string description: string packageName: string isBuiltin: boolean isOfficial: boolean version: string homepage?: string author: string path?: string } @Injectable() export class PluginManagerService { logger: Logger builtinPluginsPath: string = (window as any).builtinPluginsPath userPluginsPath: string = (window as any).userPluginsPath installedPlugins: IPluginInfo[] = (window as any).installedPlugins npmPath: string constructor ( log: LogService, private config: ConfigService, private hostApp: HostAppService, ) { this.logger = log.create('pluginManager') this.detectPath() } async detectPath () { this.npmPath = this.config.store.npm if (await fs.exists(this.npmPath)) { return } if (this.hostApp.platform !== Platform.Windows) { let searchPaths = (await exec('bash -c -l "echo $PATH"'))[0].toString().trim().split(':') for (let searchPath of searchPaths) { if (await fs.exists(path.join(searchPath, 'npm'))) { this.logger.debug('Found npm in', searchPath) this.npmPath = path.join(searchPath, 'npm') return } } } } async isNPMInstalled (): Promise { await this.detectPath() try { await exec(`${this.npmPath} -v`) return true } catch (_) { return false } } listAvailable (query?: string): Observable { return Observable .fromPromise( axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`) ) .do(response => console.log(response.data.objects)) .map(response => response.data.objects.map(item => ({ name: item.package.name.substring(NAME_PREFIX.length), packageName: item.package.name, description: item.package.description, version: item.package.version, homepage: item.package.links.homepage, author: (item.package.author || {}).name, isOfficial: item.package.publisher.username === OFFICIAL_NPM_ACCOUNT, }))) .map(plugins => plugins.filter(x => x.packageName.startsWith(NAME_PREFIX))) } async installPlugin (plugin: IPluginInfo) { let result = await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`) console.log(result) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) this.installedPlugins.push(plugin) } async uninstallPlugin (plugin: IPluginInfo) { await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) } }