#186 - Part 1 - Initial plugin installer implementation (#1883)

* Add prettierignore

* Add yarn

* Hook up yarn script to plugin installer

* Initial implementation of plugin installer

* Fix lint issues
This commit is contained in:
Bryan Phelps 2018-03-22 09:39:27 -07:00 committed by GitHub
parent 3e397df022
commit f4e627007e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 142330 additions and 0 deletions

View File

@ -1,2 +1,3 @@
package.json
vim/core/oni-plugin-typescript/package.json
lib/yarn/*

View File

@ -0,0 +1,162 @@
/**
* PluginInstaller.ts
*
* Responsible for installing, updating, and uninstalling plugins.
*/
import * as fs from "fs"
import * as path from "path"
import { Event, IEvent } from "oni-types"
// import * as Oni from "oni-api"
import { getUserConfigFolderPath } from "./../Services/Configuration"
// import { IContributions } from "./Api/Capabilities"
// import { AnonymousPlugin } from "./AnonymousPlugin"
// import { Plugin } from "./Plugin"
import { FileSystem, IFileSystem } from "./../Services/Explorer/ExplorerFileSystem"
import Process from "./Api/Process"
import * as Log from "./../Log"
/**
* Plugin identifier:
* - For _git_, this should be of the form `welle/targets.vim`
* - For _npm_, this should be the name of the module, `oni-plugin-tslint`
*/
export type PluginIdentifier = string
export interface IPluginInstallerOperationEvent {
type: "install" | "uninstall"
identifier: string
error?: Error
}
export interface IPluginInstaller {
onOperationStarted: IEvent<IPluginInstallerOperationEvent>
onOperationCompleted: IEvent<IPluginInstallerOperationEvent>
onOperationError: IEvent<IPluginInstallerOperationEvent>
install(pluginInfo: PluginIdentifier): Promise<void>
uninstall(pluginInfo: PluginIdentifier): Promise<void>
}
export class YarnPluginInstaller implements IPluginInstaller {
private _onOperationStarted = new Event<IPluginInstallerOperationEvent>()
private _onOperationCompleted = new Event<IPluginInstallerOperationEvent>()
private _onOperationError = new Event<IPluginInstallerOperationEvent>()
public get onOperationStarted(): IEvent<IPluginInstallerOperationEvent> {
return this._onOperationStarted
}
public get onOperationCompleted(): IEvent<IPluginInstallerOperationEvent> {
return this._onOperationCompleted
}
public get onOperationError(): IEvent<IPluginInstallerOperationEvent> {
return this._onOperationError
}
constructor(private _fileSystem: IFileSystem = new FileSystem(fs)) {}
public async install(identifier: string): Promise<void> {
const eventInfo: IPluginInstallerOperationEvent = {
type: "install",
identifier,
}
try {
this._onOperationStarted.dispatch(eventInfo)
await this._ensurePackageJsonIsCreated()
await this._runYarnCommand("add", [identifier])
this._onOperationCompleted.dispatch(eventInfo)
} catch (ex) {
this._onOperationError.dispatch({
...eventInfo,
error: ex,
})
}
}
public async uninstall(identifier: string): Promise<void> {
const eventInfo: IPluginInstallerOperationEvent = {
type: "uninstall",
identifier,
}
try {
this._onOperationStarted.dispatch(eventInfo)
await this._runYarnCommand("remove", [identifier])
this._onOperationCompleted.dispatch(eventInfo)
} catch (ex) {
this._onOperationError.dispatch({
...eventInfo,
error: ex,
})
}
}
private async _ensurePackageJsonIsCreated(): Promise<void> {
const packageJsonFile = this._getPackageJsonFile()
Log.info(
`[YarnPluginInstaller::_ensurePackageJsonIsCreated] - checking file: ${packageJsonFile}`,
)
const doesPackageFileExist = await this._fileSystem.exists(packageJsonFile)
if (!doesPackageFileExist) {
Log.info(
`[YarnPluginInstaller::_ensurePackageJsonIsCreated] - package file does not exist, initializing.`,
)
await this._runYarnCommand("init", ["-y"])
Log.info(
`[YarnPluginInstaller::_ensurePackageJsonIsCreated] - package file created successfully.`,
)
} else {
Log.info(
`[YarnPluginInstaller::_ensurePackageJsonIsCreated] - package file is available.`,
)
}
}
private async _runYarnCommand(command: string, args: string[]): Promise<void> {
const yarnPath = this._getYarnPath()
const workingDirectory = getUserConfigFolderPath()
const pluginDirectory = this._getPluginsFolder()
return new Promise<void>((resolve, reject) => {
Process.execNodeScript(
yarnPath,
["--modules-folder", pluginDirectory, "--production", "true", command, ...args],
{ cwd: workingDirectory },
(err: any, stdout: string, stderr: string) => {
if (err) {
Log.error("Error installing: " + stderr)
reject(err)
return
}
resolve()
},
)
})
}
private _getPackageJsonFile(): string {
return path.join(getUserConfigFolderPath(), "package.json")
}
private _getPluginsFolder(): string {
return path.join(getUserConfigFolderPath(), "plugins")
}
private _getYarnPath(): string {
return path.join(__dirname, "lib", "yarn", "yarn-1.5.1.js")
}
}

View File

@ -15,16 +15,23 @@ const extensionsRoot = path.join(__dirname, "extensions")
import { flatMap } from "./../Utility"
import { IPluginInstaller, YarnPluginInstaller } from "./PluginInstaller"
export class PluginManager implements Oni.IPluginManager {
private _rootPluginPaths: string[] = []
private _plugins: Plugin[] = []
private _anonymousPlugin: AnonymousPlugin
private _pluginsActivated: boolean = false
private _installer: IPluginInstaller = new YarnPluginInstaller()
public get plugins(): Plugin[] {
return this._plugins
}
public get installer(): IPluginInstaller {
return this._installer
}
constructor(private _config: Configuration) {}
public discoverPlugins(): void {

View File

@ -14,6 +14,7 @@ import { FolderOrFile } from "./ExplorerStore"
*/
export interface IFileSystem {
readdir(fullPath: string): Promise<FolderOrFile[]>
exists(fullPath: string): Promise<boolean>
}
export class FileSystem implements IFileSystem {
@ -40,4 +41,12 @@ export class FileSystem implements IFileSystem {
return Promise.resolve(filesAndFolders)
}
public exists(fullPath: string): Promise<boolean> {
return new Promise((resolve, reject) => {
this._fs.exists(fullPath, (exists: boolean) => {
resolve(exists)
})
})
}
}

142151
lib/yarn/yarn-1.5.1.js Normal file

File diff suppressed because one or more lines are too long