diff --git a/docs/runtime.md b/docs/runtime.md index 4153a9ec..7a0ec588 100644 --- a/docs/runtime.md +++ b/docs/runtime.md @@ -19,7 +19,7 @@ await build() .load('~/Desktop/movie/quote') .load('~/Desktop/movie/credits') .loadAll('~/Downloads/VariousMoviePlugins') - .loadAll('./node_modules', 'movies-*') + .loadAll('./node_modules', { matching: 'movies-*', hidden: true }) .token('commandName', 'cliCommand') .token('commandDescription', 'cliDescription') .token('extensionName', 'contextExtension') @@ -81,9 +81,17 @@ You can also load all immediate sub-directories located within it a directory. Load all supports a `fs-jetpack` [matching pattern](https://github.com/szwacz/fs-jetpack#findpath-searchoptions) so you can filter out a subset of directories instead of just all. ```js - .loadAll('node_modules', 'movies-*') + .loadAll('node_modules', { matching: 'movies-*' }) ``` +If you would like to keep plugins hidden and not available at the command line: + +```js + .loadAll('node_modules', { matching: 'movies-*', hidden: true }) +``` + +When plugins are hidden they can still be run directly from the runtime. + # Finishing Configuration diff --git a/packages/gluegun/src/cli/banner.txt b/packages/gluegun/src/cli/banner.txt deleted file mode 100644 index 3e343f10..00000000 --- a/packages/gluegun/src/cli/banner.txt +++ /dev/null @@ -1,8 +0,0 @@ - ___ _ - | | (_ ) - |:::| __ | | _ _ __ __ _ _ ___ - _)=(______ /'_ `\ | | ( ) ( ) /'__`\ /'_ `\( ) ( )/' _ `\ -~Y/ =====-__|==~<( (_) | | | | (_) |( ___/( (_) || (_) || ( ) | - / |L)~~~ `\__ |(___)`\___/'`\____)`\__ |`\___/'(_) (_) - |_| ( )_) | ( )_) | - \___/' \___/' \ No newline at end of file diff --git a/packages/gluegun/src/cli/index.js b/packages/gluegun/src/cli/index.js deleted file mode 100644 index 27ca8e4e..00000000 --- a/packages/gluegun/src/cli/index.js +++ /dev/null @@ -1,90 +0,0 @@ -const parseCommandLine = require('./parse-command-line') -const { forEach, map, pipe, propOr, tryCatch, always, concat } = require('ramda') -const jetpack = require('fs-jetpack') -const Runtime = require('../domain/runtime') -const getPluginsFromConfig = require('../loaders/plugins-from-config') -const homePluginDirectories = require('../loaders/home-plugin-directories') -const projectPluginDirectories = require('../loaders/project-plugin-directories') -const toml = require('toml') -const printCommands = require('./print-commands') -const printWtf = require('./print-wtf') -const printBrandHeader = require('./print-brand-header') - -/** - * Our main entry point. - * - * This is async because our runtime execution is. - */ -async function run () { - const cwd = jetpack.cwd() - - // parse the command line into what we need - let { - name, - args, - options, - brand = 'gluegun', - wtf - } = parseCommandLine(process.argv) - - // create the runtime - const runtime = new Runtime(brand) - - // --- PLUGINS #2 - $HOME/.${brand}/plugins/* - forEach( - runtime.load, - homePluginDirectories(brand) - ) - - // --- PLUGINS #3 - in .//plugins and .//plugins-remote - const projectBrandDir = `${cwd}/${brand}` - forEach( - runtime.load, - projectPluginDirectories(projectBrandDir) - ) - - // --- PLUGINS #4 - in .//.toml in the plugins key - const morePlugins = map( - concat(`${cwd}/${brand}/`), - getPluginsFromConfig(`${projectBrandDir}/${brand}.toml`) - ) - forEach(runtime.load, morePlugins) - - // load the config - runtime.defaults = pipe( - jetpack.read, - tryCatch(toml.parse, always({})), - propOr({}, 'defaults') - )(`${cwd}/${brand}.toml`) - - // in branded mode, let's see if there's match prepending the - // branded name to the front and searching for a command - const brandPlugin = runtime.findPlugin(brand) - const foundCommand = runtime.findCommand(brandPlugin, `${name} ${args}`) - // we found a short cut via the brand, so let's doctor the input to target the right thing - if (foundCommand) { - args = `${name} ${args}` - name = brand - } - - // wtf mode - if (wtf) { - printWtf(runtime) - return - } - - // print the header - if (brand !== 'gluegun') { - printBrandHeader(runtime, brandPlugin) - } - - // let's do this! - const context = await runtime.run(name, args, options) - - // print the command list - if (!context.command) { - printCommands(context, brandPlugin) - } -} - -run() diff --git a/packages/gluegun/src/cli/print-commands.js b/packages/gluegun/src/cli/print-commands.js index c9db3f8d..2f135ebb 100644 --- a/packages/gluegun/src/cli/print-commands.js +++ b/packages/gluegun/src/cli/print-commands.js @@ -17,6 +17,11 @@ const { isBlank } = require('../utils/string-utils') */ const hasLoadStateError = propEq('loadState', 'error') +/** + * Is this a hidden command? + */ +const isHidden = propEq('hidden', true) + /** * Gets the list of plugins. * @@ -27,6 +32,7 @@ function getListOfPlugins (context) { return pipe( dotPath('runtime.plugins'), reject(hasLoadStateError), + reject(isHidden), reject(plugin => context.runtime.defaultPlugin && context.runtime.defaultPlugin.name === plugin.name), sortBy(prop('name')), map(plugin => [ @@ -47,6 +53,7 @@ function getListOfCommands (context, plugin) { return pipe( prop('commands'), reject(hasLoadStateError), + reject(isHidden), map(command => [ command.name, replace('$BRAND', context.runtime.brand, command.description || '-') diff --git a/packages/gluegun/src/domain/builder.js b/packages/gluegun/src/domain/builder.js index 04b1a12b..d405a970 100644 --- a/packages/gluegun/src/domain/builder.js +++ b/packages/gluegun/src/domain/builder.js @@ -53,9 +53,9 @@ class Builder { // the plugins get loaded last this.loads.forEach(entry => { switch (entry.type) { - case 'loadDefault': runtime.loadDefault(entry.value); break - case 'load': runtime.load(entry.value); break - case 'loadAll': runtime.loadAll(entry.value, entry.matching); break + case 'loadDefault': runtime.loadDefault(entry.value, entry.options); break + case 'load': runtime.load(entry.value, entry.options); break + case 'loadAll': runtime.loadAll(entry.value, entry.options); break } }) @@ -87,34 +87,36 @@ class Builder { /** * Add a plugin to the list. * - * @value {string} The plugin directory. - * @return {Builder} self. + * @param {string} value The plugin directory. + * @param {Object} options Additional loading options. + * @return {Builder} self. */ - load (value) { - this.loads.push({ type: 'load', value }) + load (value, options = {}) { + this.loads.push({ type: 'load', value, options }) return this } /** * Add a default plugin to the list. * - * @value {string} The default plugin directory. - * @return {Builder} self. + * @param {string} value The default plugin directory. + * @param {Object} options Additional loading options. + * @return {Builder} self. */ - loadDefault (value) { - this.loads.push({ type: 'loadDefault', value }) + loadDefault (value, options = {}) { + this.loads.push({ type: 'loadDefault', value, options }) return this } /** * Add a plugin group to the list. * - * @value {string} The directory with sub-directories. - * @matching {string} An optional jetpack glob matching to match. - * @return {Builder} self. + * @param {string} value The directory with sub-directories. + * @param {Object} options Additional loading options. + * @return {Builder} self. */ - loadAll (value, matching) { - this.loads.push({ type: 'loadAll', value, matching }) + loadAll (value, options = {}) { + this.loads.push({ type: 'loadAll', value, options }) return this } diff --git a/packages/gluegun/src/domain/command.js b/packages/gluegun/src/domain/command.js index 50c6a702..865854eb 100644 --- a/packages/gluegun/src/domain/command.js +++ b/packages/gluegun/src/domain/command.js @@ -31,6 +31,7 @@ class Command { this.loadState = 'none' this.errorState = 'none' this.exception = null + this.hidden = false } } diff --git a/packages/gluegun/src/domain/plugin.js b/packages/gluegun/src/domain/plugin.js index d79296f9..d1e08afa 100644 --- a/packages/gluegun/src/domain/plugin.js +++ b/packages/gluegun/src/domain/plugin.js @@ -31,6 +31,7 @@ class Plugin { this.defaults = {} this.directory = null this.errorMessage = null + this.hidden = false /** * A list of commands. */ diff --git a/packages/gluegun/src/domain/runtime.js b/packages/gluegun/src/domain/runtime.js index 55a2dbfd..c2f483b7 100644 --- a/packages/gluegun/src/domain/runtime.js +++ b/packages/gluegun/src/domain/runtime.js @@ -15,6 +15,7 @@ const { append, forEach, isNil, + dissoc, map, is } = require('ramda') @@ -199,16 +200,18 @@ class Runtime { * Loads a plugin from a directory. * * @param {string} directory The directory to load from. + * @param {Object} options Additional loading options. * @return {Plugin} A plugin. */ - load (directory) { + load (directory, options = {}) { const { brand, extensionNameToken, commandNameToken, commandDescriptionToken } = this const plugin = loadPluginFromDirectory(directory, { extensionNameToken, commandNameToken, commandDescriptionToken, - brand + brand, + hidden: options['hidden'] }) this.plugins = append(plugin, this.plugins) @@ -223,10 +226,11 @@ class Runtime { * Loads a plugin from a directory and sets it as the default. * * @param {string} directory The directory to load from. + * @param {Object} options Additional loading options. * @return {Plugin} A plugin. */ - loadDefault (directory) { - const plugin = this.load(directory) + loadDefault (directory, options = {}) { + const plugin = this.load(directory, options) this.defaultPlugin = plugin return plugin } @@ -235,14 +239,14 @@ class Runtime { * Loads a bunch of plugins from the immediate sub-directories of a directory. * * @param {string} directory The directory to grab from. - * @param {string} matching A jetpack matching pattern to filter the list. + * @param {Object} options Addition loading options. * @return {Plugin[]} A bunch of plugins */ - loadAll (directory, matching = '*') { + loadAll (directory, options = {}) { if (isBlank(directory) || !isDirectory(directory)) return [] return pipe( - dir => subdirectories(dir, false, matching), - map(this.load) + dir => subdirectories(dir, false, options['matching']), + map(dir => this.load(dir, dissoc('matching', options))) )(directory) } diff --git a/packages/gluegun/src/loaders/toml-plugin-loader.js b/packages/gluegun/src/loaders/toml-plugin-loader.js index 511ccedb..133b378d 100644 --- a/packages/gluegun/src/loaders/toml-plugin-loader.js +++ b/packages/gluegun/src/loaders/toml-plugin-loader.js @@ -4,7 +4,7 @@ const loadCommandFromFile = require('./command-loader') const loadExtensionFromFile = require('./extension-loader') const { isNotDirectory } = require('../utils/filesystem-utils') const { isBlank } = require('../utils/string-utils') -const { map, contains, __ } = require('ramda') +const { assoc, map, contains, __ } = require('ramda') const toml = require('toml') /** @@ -28,12 +28,15 @@ function loadFromDirectory (directory, options = {}) { brand = 'gluegun', commandFilePattern = '*.js', extensionFilePattern = '*.js', + hidden = false, commandNameToken, commandDescriptionToken, extensionNameToken, name } = options + plugin.hidden = Boolean(options.hidden) + if (!isBlank(name)) { plugin.name = name } @@ -115,6 +118,9 @@ function loadFromDirectory (directory, options = {}) { plugin.loadState = 'ok' plugin.errorState = 'none' + // set the hidden bit + plugin.commands = map(assoc('hidden', hidden), plugin.commands) + return plugin } diff --git a/packages/gluegun/test/domain/builder.test.js b/packages/gluegun/test/domain/builder.test.js index 814803df..8d7524c4 100644 --- a/packages/gluegun/test/domain/builder.test.js +++ b/packages/gluegun/test/domain/builder.test.js @@ -12,7 +12,7 @@ test('the gauntlet', t => { // plugins .loadDefault(`${__dirname}/../fixtures/good-plugins/threepack`) .load(`${__dirname}/../fixtures/good-plugins/simplest`) - .loadAll(`${__dirname}/../fixtures/good-plugins`) + .loadAll(`${__dirname}/../fixtures/good-plugins`, { hidden: true }) // events .on('start', () => {}) diff --git a/packages/gluegun/test/domain/command.test.js b/packages/gluegun/test/domain/command.test.js index bfd9a910..89c6a974 100644 --- a/packages/gluegun/test/domain/command.test.js +++ b/packages/gluegun/test/domain/command.test.js @@ -8,6 +8,7 @@ test('default state', t => { t.falsy(command.file) t.falsy(command.description) t.falsy(command.run) + t.is(command.hidden, false) t.is(command.loadState, 'none') t.is(command.errorState, 'none') }) diff --git a/packages/gluegun/test/domain/plugin.test.js b/packages/gluegun/test/domain/plugin.test.js index b7d47db1..029b19ea 100644 --- a/packages/gluegun/test/domain/plugin.test.js +++ b/packages/gluegun/test/domain/plugin.test.js @@ -8,6 +8,7 @@ test('default state', t => { t.is(plugin.errorState, 'none') t.falsy(plugin.directory) t.falsy(plugin.name) + t.is(plugin.hidden, false) t.deepEqual(plugin.commands, []) t.deepEqual(plugin.extensions, []) t.deepEqual(plugin.defaults, {}) diff --git a/packages/gluegun/test/domain/runtime-load.test.js b/packages/gluegun/test/domain/runtime-load.test.js index 4a3cf397..153bc497 100644 --- a/packages/gluegun/test/domain/runtime-load.test.js +++ b/packages/gluegun/test/domain/runtime-load.test.js @@ -8,6 +8,13 @@ test('load a directory', t => { t.is(r.plugins.length, 2) }) +test('hides commands', t => { + const r = new Runtime() + r.load(`${__dirname}/../fixtures/good-plugins/threepack`, { hidden: true }) + t.is(r.plugins.length, 1) + t.true(r.plugins[0].commands[2].hidden) +}) + test('allows plugins with broken dirs', t => { const r = new Runtime() r.load(__filename) diff --git a/packages/gluegun/test/domain/runtime-loadAll.test.js b/packages/gluegun/test/domain/runtime-loadAll.test.js index f9f2e110..b62576f0 100644 --- a/packages/gluegun/test/domain/runtime-loadAll.test.js +++ b/packages/gluegun/test/domain/runtime-loadAll.test.js @@ -4,7 +4,21 @@ const Runtime = require('../../src/domain/runtime') test('loads all sub-directories', t => { const r = new Runtime() r.loadAll(`${__dirname}/../fixtures/good-plugins`) - t.is(r.plugins.length, 10) + + t.is(r.plugins.length, 11) +}) + +test('matches sub-directories', t => { + const r = new Runtime() + r.loadAll(`${__dirname}/../fixtures/good-plugins`, { matching: 'blank-*' }) + t.is(r.plugins.length, 1) +}) + +test('hides commands', t => { + const r = new Runtime() + r.loadAll(`${__dirname}/../fixtures/good-plugins`, { matching: 'threepack', hidden: true }) + t.is(r.plugins.length, 1) + t.true(r.plugins[0].commands[2].hidden) }) test('loadAll ignores bad directories', t => { diff --git a/packages/gluegun/test/fixtures/good-plugins/hidden/commands/hide.js b/packages/gluegun/test/fixtures/good-plugins/hidden/commands/hide.js new file mode 100644 index 00000000..a8b270be --- /dev/null +++ b/packages/gluegun/test/fixtures/good-plugins/hidden/commands/hide.js @@ -0,0 +1,5 @@ +async function hide () { + return 1 +} + +module.exports = hide diff --git a/packages/gluegun/test/loaders/toml-plugin-loader.test.js b/packages/gluegun/test/loaders/toml-plugin-loader.test.js index 3a3e6946..661bee96 100644 --- a/packages/gluegun/test/loaders/toml-plugin-loader.test.js +++ b/packages/gluegun/test/loaders/toml-plugin-loader.test.js @@ -62,6 +62,9 @@ test('loads commands', async t => { t.is(two.file, `${dir}/commands/two.js`) t.is(typeof two.run, 'function') t.is(await two.run(), 'two') + t.false(plugin.commands[0].hidden) + t.false(plugin.commands[1].hidden) + t.false(plugin.commands[2].hidden) }) test('load commands with front matter', async t => { @@ -106,3 +109,14 @@ test('blank names fallback to directory name', t => { t.is(plugin.loadState, 'ok') t.is(plugin.errorState, 'none') }) + +test('supports hidden plugins & commands', t => { + const dir = `${__dirname}/../fixtures/good-plugins/threepack` + const plugin = load(dir, { hidden: true }) + + t.true(plugin.hidden) + t.true(plugin.commands[0].hidden) + t.true(plugin.commands[1].hidden) + t.true(plugin.commands[2].hidden) +}) +