bundle NPM with Terminus (fixes #584, fixes #10)

This commit is contained in:
Eugene Pankov 2019-02-13 21:35:37 +01:00
parent 808e7f4699
commit 59d1a2fc23
6 changed files with 2839 additions and 80 deletions

View File

@ -31,13 +31,14 @@
"@angular/forms": "4.0.1", "@angular/forms": "4.0.1",
"@angular/platform-browser": "4.0.1", "@angular/platform-browser": "4.0.1",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22", "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22",
"rxjs": "5.3.0",
"terminus-core": "*", "terminus-core": "*",
"terminus-settings": "*", "terminus-settings": "*"
"rxjs": "5.3.0"
}, },
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
"mz": "^2.6.0" "mz": "^2.6.0",
"npm": "^6.7.0"
}, },
"false": {} "false": {}
} }

View File

@ -20,7 +20,7 @@
small {{plugin.description}} small {{plugin.description}}
button.btn.btn-primary.ml-2( button.btn.btn-primary.ml-2(
*ngIf='npmInstalled && knownUpgrades[plugin.name]', *ngIf='knownUpgrades[plugin.name]',
(click)='upgradePlugin(plugin)', (click)='upgradePlugin(plugin)',
[disabled]='busy[plugin.name] != undefined' [disabled]='busy[plugin.name] != undefined'
) )
@ -42,24 +42,13 @@
button.btn.btn-danger.ml-2( button.btn.btn-danger.ml-2(
(click)='uninstallPlugin(plugin)', (click)='uninstallPlugin(plugin)',
*ngIf='!plugin.isBuiltin && npmInstalled', *ngIf='!plugin.isBuiltin',
[disabled]='busy[plugin.name] != undefined' [disabled]='busy[plugin.name] != undefined'
) )
i.fas.fa-fw.fa-trash(*ngIf='busy[plugin.name] != BusyState.Uninstalling') i.fas.fa-fw.fa-trash(*ngIf='busy[plugin.name] != BusyState.Uninstalling')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling') i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling')
.text-center.mt-5(*ngIf='npmMissing') div
h4 npm not installed
p.mb-2 npm is required to install Terminus plugins.
.btn-group
button.btn.btn-outline-primary((click)='downloadNPM()')
i.fas.fa-download
span Get npm
button.btn.btn-outline-info((click)='checkNPM()')
i.fas.fa-refresh
span Try again
div(*ngIf='npmInstalled')
h3.mt-4 Available h3.mt-4 Available
.input-group.mb-3 .input-group.mb-3

View File

@ -21,8 +21,6 @@ export class PluginsSettingsTabComponent {
@Input() busy: {[id: string]: BusyState} = {} @Input() busy: {[id: string]: BusyState} = {}
@Input() erroredPlugin: string @Input() erroredPlugin: string
@Input() errorMessage: string @Input() errorMessage: string
@Input() npmInstalled = false
@Input() npmMissing = false
constructor ( constructor (
private electron: ElectronService, private electron: ElectronService,
@ -50,22 +48,12 @@ export class PluginsSettingsTabComponent {
this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semver.gt(x.version, plugin.version)) this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semver.gt(x.version, plugin.version))
} }
}) })
this.checkNPM()
} }
openPluginsFolder (): void { openPluginsFolder (): void {
this.hostApp.getShell().openItem(this.pluginManager.userPluginsPath) this.hostApp.getShell().openItem(this.pluginManager.userPluginsPath)
} }
downloadNPM (): void {
this.hostApp.getShell().openExternal('https://nodejs.org/en/download/current/')
}
async checkNPM () {
this.npmInstalled = await this.pluginManager.isNPMInstalled()
this.npmMissing = !this.npmInstalled
}
searchAvailable (query: string) { searchAvailable (query: string) {
this.availablePluginsQuery$.next(query) this.availablePluginsQuery$.next(query)
} }

View File

@ -1,11 +1,9 @@
import * as path from 'path' import npm from 'npm'
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import axios from 'axios' import axios from 'axios'
import { Observable, from } from 'rxjs' import { Observable, from } from 'rxjs'
import { map } from 'rxjs/operators' import { map } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Logger, LogService, ConfigService, HostAppService, Platform } from 'terminus-core' import { Logger, LogService } from 'terminus-core'
const NAME_PREFIX = 'terminus-' const NAME_PREFIX = 'terminus-'
const KEYWORD = 'terminus-plugin' const KEYWORD = 'terminus-plugin'
@ -29,45 +27,23 @@ export class PluginManagerService {
builtinPluginsPath: string = (window as any).builtinPluginsPath builtinPluginsPath: string = (window as any).builtinPluginsPath
userPluginsPath: string = (window as any).userPluginsPath userPluginsPath: string = (window as any).userPluginsPath
installedPlugins: IPluginInfo[] = (window as any).installedPlugins installedPlugins: IPluginInfo[] = (window as any).installedPlugins
npmPath: string
private envPath: string private npmReady: Promise<void>
constructor ( constructor (
log: LogService, log: LogService,
private config: ConfigService,
private hostApp: HostAppService,
) { ) {
this.logger = log.create('pluginManager') this.logger = log.create('pluginManager')
this.detectPath() this.npmReady = new Promise(resolve => {
} npm.load({
prefix: this.userPluginsPath,
async detectPath () { }, err => {
this.npmPath = this.config.store.npm if (err) {
this.envPath = process.env.PATH this.logger.error(err)
if (await fs.exists(this.npmPath)) {
return
}
if (this.hostApp.platform !== Platform.Windows) {
this.envPath = (await exec('$SHELL -i -c \'echo $PATH\''))[0].toString().trim()
let searchPaths = this.envPath.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<boolean> {
await this.detectPath()
try {
await exec(`${this.npmPath} -v`, { env: this.getEnv() })
return true
} catch (_) {
return false
} }
resolve()
})
})
} }
listAvailable (query?: string): Observable<IPluginInfo[]> { listAvailable (query?: string): Observable<IPluginInfo[]> {
@ -92,17 +68,23 @@ export class PluginManagerService {
} }
async installPlugin (plugin: IPluginInfo) { async installPlugin (plugin: IPluginInfo) {
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`, { env: this.getEnv() }) await this.npmReady
npm.commands.install([`${plugin.packageName}@${plugin.version}`], err => {
if (err) {
this.logger.error(err)
}
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
this.installedPlugins.push(plugin) this.installedPlugins.push(plugin)
})
} }
async uninstallPlugin (plugin: IPluginInfo) { async uninstallPlugin (plugin: IPluginInfo) {
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`, { env: this.getEnv() }) await this.npmReady
npm.commands.remove([plugin.packageName], err => {
if (err) {
this.logger.error(err)
}
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
} })
private getEnv (): any {
return Object.assign(process.env, { PATH: this.envPath })
} }
} }

View File

@ -44,6 +44,7 @@ module.exports = {
externals: [ externals: [
'fs', 'fs',
'font-manager', 'font-manager',
'npm',
'path', 'path',
'mz/fs', 'mz/fs',
'mz/child_process', 'mz/child_process',

File diff suppressed because it is too large Load Diff