-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Node bindings): Add plugins module
- Loading branch information
Showing
7 changed files
with
219 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * as plugins from './plugins' | ||
export * as config from './config' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { fromJSON } from './prelude' | ||
|
||
const addon = require('../native') | ||
|
||
export type Installation = 'binary' | 'docker' | 'package' | ||
|
||
export interface Plugin { | ||
// Properties from the plugin's codemeta.json file | ||
name: string | ||
softwareVersion: string | ||
description: string | ||
installUrl: string[] | ||
featureList: Record<string, unknown>[] | ||
|
||
// If installed, the installation type | ||
installation?: Installation | ||
|
||
// The current alias for this plugin, if any | ||
alias?: string | ||
} | ||
|
||
/** | ||
* List the installed plugins | ||
* | ||
* @returns An array of plugins | ||
*/ | ||
export function list(): Plugin[] { | ||
return fromJSON<Plugin[]>(addon.pluginsList()) | ||
} | ||
|
||
/** | ||
* Install a plugin | ||
* | ||
* @param spec A plugin identifier e.g. `javascript@0.50.1` | ||
* @param installations An array of installation methods to try | ||
* @return An array of installed plugins | ||
*/ | ||
export function install( | ||
spec: string, | ||
installations?: Installation | Installation[] | ||
): Plugin[] { | ||
return fromJSON<Plugin[]>(addon.pluginsInstall(spec, installations ?? [])) | ||
} | ||
|
||
/** | ||
* Uninstall a plugin | ||
* | ||
* @param alias The alias or name of the plugin | ||
* @returns An array of installed plugins | ||
*/ | ||
export function uninstall(alias: string): Plugin[] { | ||
return fromJSON<Plugin[]>(addon.pluginsUninstall(alias)) | ||
} | ||
|
||
/** | ||
* Upgrade a plugin | ||
* | ||
* @param spec A plugin identifier e.g. `javascript` | ||
* @return An array of installed plugins | ||
*/ | ||
export function upgrade(spec: string): Plugin[] { | ||
return fromJSON<Plugin[]>(addon.pluginsUpgrade(spec)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,20 @@ | ||
use neon::prelude::*; | ||
|
||
mod config; | ||
mod plugins; | ||
mod prelude; | ||
|
||
register_module!(mut cx, { | ||
cx.export_function("pluginsList", plugins::list)?; | ||
cx.export_function("pluginsInstall", plugins::install)?; | ||
cx.export_function("pluginsUninstall", plugins::uninstall)?; | ||
cx.export_function("pluginsUpgrade", plugins::upgrade)?; | ||
|
||
cx.export_function("configRead", config::read)?; | ||
cx.export_function("configWrite", config::write)?; | ||
cx.export_function("configValidate", config::validate)?; | ||
cx.export_function("configSet", config::set)?; | ||
cx.export_function("configReset", config::reset)?; | ||
|
||
Ok(()) | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use crate::{ | ||
config::{self}, | ||
prelude::*, | ||
}; | ||
use neon::{prelude::*, result::Throw}; | ||
use std::str::FromStr; | ||
use std::sync::{Mutex, MutexGuard}; | ||
use stencila::{ | ||
config::Config, | ||
once_cell::sync::Lazy, | ||
plugins::{self, Installation, Plugin, Plugins}, | ||
}; | ||
|
||
/// A global plugins store | ||
/// | ||
/// The plugins store needs to be read on startup and then passed to various | ||
/// functions in other modules on each invocation (for delegation to each plugin). | ||
/// As for config, we want to avoid exposing that implementation detail in these bindings | ||
/// so have this global mutable plugins store that gets loaded when the module is loaded, | ||
/// updated in the functions below and then passed on to other functions | ||
pub static PLUGINS: Lazy<Mutex<Plugins>> = | ||
Lazy::new(|| Mutex::new(Plugins::load().expect("Unable to load plugins"))); | ||
|
||
/// Obtain the plugins store | ||
pub fn obtain(cx: &mut FunctionContext) -> NeonResult<MutexGuard<'static, Plugins>> { | ||
match PLUGINS.try_lock() { | ||
Ok(guard) => Ok(guard), | ||
Err(error) => cx.throw_error(format!( | ||
"When attempting on obtain plugins: {}", | ||
error.to_string() | ||
)), | ||
} | ||
} | ||
|
||
/// List plugins | ||
pub fn list(mut cx: FunctionContext) -> JsResult<JsString> { | ||
let aliases = &config::obtain(&mut cx)?.plugins.aliases; | ||
let plugins = &*obtain(&mut cx)?; | ||
|
||
to_json(cx, plugins.list_plugins(aliases)) | ||
} | ||
|
||
/// Install a plugin | ||
pub fn install(mut cx: FunctionContext) -> JsResult<JsString> { | ||
let spec = &cx.argument::<JsString>(0)?.value(); | ||
|
||
let config = &config::obtain(&mut cx)?; | ||
let installs = &installations(&mut cx, 1, &config)?; | ||
let aliases = &config.plugins.aliases; | ||
let plugins = &mut *obtain(&mut cx)?; | ||
|
||
match runtime(&mut cx)? | ||
.block_on(async { Plugin::install(spec, installs, aliases, plugins, None).await }) | ||
{ | ||
Ok(_) => to_json(cx, plugins.list_plugins(aliases)), | ||
Err(error) => cx.throw_error(error.to_string()), | ||
} | ||
} | ||
|
||
/// Uninstall a plugin | ||
pub fn uninstall(mut cx: FunctionContext) -> JsResult<JsString> { | ||
let alias = &cx.argument::<JsString>(0)?.value(); | ||
let aliases = &config::obtain(&mut cx)?.plugins.aliases; | ||
let plugins = &mut *obtain(&mut cx)?; | ||
|
||
match Plugin::uninstall(alias, aliases, plugins) { | ||
Ok(_) => to_json(cx, plugins.list_plugins(aliases)), | ||
Err(error) => cx.throw_error(error.to_string()), | ||
} | ||
} | ||
|
||
/// Upgrade a plugin | ||
pub fn upgrade(mut cx: FunctionContext) -> JsResult<JsString> { | ||
let spec = &cx.argument::<JsString>(0)?.value(); | ||
let config = &config::obtain(&mut cx)?; | ||
let installs = &config.plugins.installations; | ||
let aliases = &config.plugins.aliases; | ||
let plugins = &mut *obtain(&mut cx)?; | ||
|
||
match runtime(&mut cx)? | ||
.block_on(async { Plugin::upgrade(spec, installs, aliases, plugins).await }) | ||
{ | ||
Ok(_) => to_json(cx, plugins.list_plugins(aliases)), | ||
Err(error) => cx.throw_error(error.to_string()), | ||
} | ||
} | ||
|
||
/// Get the `installations` argument, falling back to the array in `config.plugins.installations` | ||
pub fn installations( | ||
cx: &mut FunctionContext, | ||
position: i32, | ||
config: &Config, | ||
) -> Result<Vec<Installation>, Throw> { | ||
let arg = cx.argument::<JsArray>(position)?.to_vec(cx)?; | ||
if arg.is_empty() { | ||
Ok(config.plugins.installations.clone()) | ||
} else { | ||
let mut installations = Vec::new(); | ||
for value in arg { | ||
let str = value.to_string(cx)?.value(); | ||
let installation = match plugins::Installation::from_str(&str) { | ||
Ok(value) => value, | ||
Err(error) => return cx.throw_error(error.to_string()), | ||
}; | ||
installations.push(installation) | ||
} | ||
Ok(installations) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { list, install, uninstall, upgrade } from '../lib/plugins' | ||
|
||
describe('plugins', () => { | ||
test('list', () => { | ||
expect(list()).toEqual(expect.arrayContaining([])) | ||
}) | ||
|
||
test('install', () => { | ||
expect(install('javascript')).toEqual(expect.arrayContaining([])) | ||
}) | ||
|
||
test('uninstall', () => { | ||
expect(uninstall('javascript')).toEqual(expect.arrayContaining([])) | ||
}) | ||
|
||
test('upgrade', () => { | ||
expect(upgrade('javascript')).toEqual(expect.arrayContaining([])) | ||
}) | ||
}) |