From 9d695d8feded0eb8654ec09c744257678bbb7cea Mon Sep 17 00:00:00 2001 From: Bhargav Ponnapalli Date: Mon, 17 Jul 2017 22:18:44 +0530 Subject: [PATCH] feat(cli): Added util to display help for a specific script (#151) * Added util to display help for a specific script * Remove fault verbose flag * Added util to display help for a specific script * Added documentation for help scripts in README * Remove help logging for specific help script --- README.md | 10 +++++ src/bin-utils/__tests__/index.js | 65 ++++++++++++++++++++++++++++++- src/bin-utils/__tests__/parser.js | 29 ++++++++++++++ src/bin-utils/index.js | 44 +++++++++++++++------ src/bin-utils/parser.js | 28 +++++++++---- 5 files changed, 156 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d19377c..4033e02 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,15 @@ build - webpack build.prod - webpack -p validate - concurrently "nps lint" "nps test" "nps build" ``` +You can also use the help command with a script name +```console +nps help test.watch +``` +Which will output the details of the script `test.watch`: + +```console +test.watch - run in the amazingly intelligent Jest watch mode - jest --watch +``` Now, to run a script, you can run: @@ -143,6 +152,7 @@ But the fun doesn't end there! You can use a prefix: ```console nps b # will run the build script +nps help b # will display help for the build script ``` And these prefixes can go as deep as you like! diff --git a/src/bin-utils/__tests__/index.js b/src/bin-utils/__tests__/index.js index 1754463..20debf1 100644 --- a/src/bin-utils/__tests__/index.js +++ b/src/bin-utils/__tests__/index.js @@ -3,7 +3,7 @@ import path from 'path' import chalk from 'chalk' import {spy} from 'sinon' import {oneLine} from 'common-tags' -import {help, preloadModule, loadConfig} from '../' +import {help, preloadModule, loadConfig, specificHelpScript} from '../' test('preloadModule: resolves a relative path', () => { // this is relative to process.cwd() I think... @@ -313,6 +313,69 @@ test('help: do not display scripts with flag hiddenFromHelp set to true', () => expect(message).toBe(expected) }) +test('specificHelpScript: help: formats a nice message', () => { + const config = { + scripts: { + foo: { + bar: { + description: 'the foo script', + script: 'echo "foo"', + }, + }, + }, + } + const actual = specificHelpScript(config, 'f.b') + const name = chalk.green('foo.bar') + const description = chalk.white('the foo script') + const script = chalk.gray('echo "foo"') + const expected = `${name} - ${description} - ${script}` + expect(actual).toBe(expected) +}) + +test('specificHelpScript: help: shows script not found when no match found', () => { + const config = { + scripts: { + foo: { + description: 'the foo script', + script: 'echo "foo"', + }, + }, + } + const actual = specificHelpScript(config, 'w') + const expected = chalk.yellow('Script matching name w was not found.') + expect(actual).toBe(expected) +}) + +test('specificHelpScript : help: formats a nice message', () => { + const config = { + scripts: { + foo: { + description: 'the foo script', + script: 'echo "foo"', + }, + }, + } + const actual = specificHelpScript(config, 'f') + const expected = `${chalk.green('foo')} - ${chalk.white( + 'the foo script', + )} - ${chalk.gray('echo "foo"')}` + expect(actual).toBe(expected) +}) + +test('specificHelpScript : help: shows script not found when no match found', () => { + const config = { + scripts: { + foo: { + description: 'the foo script', + script: 'echo "foo"', + }, + }, + } + const actual = specificHelpScript(config, 'w') + const expected = chalk.yellow('Script matching name w was not found.') + expect(actual).toBe(expected) +}) + function getAbsoluteFixturePath(fixture) { return path.join(__dirname, 'fixtures', fixture) } diff --git a/src/bin-utils/__tests__/parser.js b/src/bin-utils/__tests__/parser.js index cebcf57..e634404 100644 --- a/src/bin-utils/__tests__/parser.js +++ b/src/bin-utils/__tests__/parser.js @@ -27,6 +27,7 @@ jest.mock('../../bin-utils', () => { } }), help: jest.fn(), + specificHelpScript: jest.fn(), clearAll, mock: {}, } @@ -199,6 +200,34 @@ test('if there is a help script in the psConfig, does not show the help', () => expect(mockGetLogger.mock.info).toHaveBeenCalledTimes(0) }) +test('if help is called with a script, it shows the help for that script', () => { + mockBinUtils.mock.psConfig = { + scripts: { + specific: { + description: 'the specific script', + script: 'echo "specific"', + }, + }, + } + expect(parse('help specific')).toBe(undefined) + expect(mockBinUtils.specificHelpScript).toHaveBeenCalledTimes(1) + expect(mockGetLogger.mock.info).toHaveBeenCalledTimes(1) +}) + +test('if help is called with a script, it shows the help for that script', () => { + mockBinUtils.mock.psConfig = { + scripts: { + specific: { + description: 'the specific script', + script: 'echo "specific"', + }, + }, + } + expect(parse('help specific')).toBe(undefined) + expect(mockBinUtils.specificHelpScript).toHaveBeenCalledTimes(1) + expect(mockGetLogger.mock.info).toHaveBeenCalledTimes(1) +}) + // https://github.com/yargs/yargs/issues/782 // we can't test this functionality reasonably with unit tests // so we've got an e2e test for it diff --git a/src/bin-utils/index.js b/src/bin-utils/index.js index a54fc5f..4da099a 100644 --- a/src/bin-utils/index.js +++ b/src/bin-utils/index.js @@ -6,6 +6,7 @@ import { isUndefined, isEmpty, isFunction, + isNull, } from 'lodash' import typeOf from 'type-detect' import chalk from 'chalk' @@ -13,6 +14,7 @@ import {safeLoad} from 'js-yaml' import {oneLine} from 'common-tags' import getLogger from '../get-logger' import {resolveScriptObjectToScript} from '../resolve-script-object-to-string' +import getScriptByPrefix from './get-script-by-prefix' import initialize from './initialize' const log = getLogger() @@ -100,7 +102,14 @@ function loadConfig(configPath, input) { return config } -export {initialize, help, getModuleRequirePath, preloadModule, loadConfig} +export { + initialize, + help, + getModuleRequirePath, + preloadModule, + loadConfig, + specificHelpScript, +} /****** implementations ******/ @@ -162,22 +171,24 @@ function requireDefaultFromModule(modulePath) { } } +function scriptObjectToChalk({name, description, script}) { + const coloredName = chalk.green(name) + const coloredScript = chalk.gray(script) + let line + if (description) { + line = [coloredName, chalk.white(description), coloredScript] + } else { + line = [coloredName, coloredScript] + } + return line.join(' - ').trim() +} + function help({scripts}) { const availableScripts = getAvailableScripts(scripts) const filteredScripts = availableScripts.filter( script => !script.hiddenFromHelp, ) - const scriptLines = filteredScripts.map(({name, description, script}) => { - const coloredName = chalk.green(name) - const coloredScript = chalk.gray(script) - let line - if (description) { - line = [coloredName, chalk.white(description), coloredScript] - } else { - line = [coloredName, coloredScript] - } - return line.join(' - ').trim() - }) + const scriptLines = filteredScripts.map(scriptObjectToChalk) if (scriptLines.length) { const topMessage = 'Available scripts (camel or kebab case accepted)' const message = `${topMessage}\n\n${scriptLines.join('\n')}` @@ -187,6 +198,15 @@ function help({scripts}) { } } +function specificHelpScript(config, scriptName) { + const script = getScriptByPrefix(config, scriptName) + if (isNull(script)) { + return chalk.yellow(`Script matching name ${scriptName} was not found.`) + } else { + return scriptObjectToChalk(script) + } +} + function getAvailableScripts(config, prefix = [], rootLevel = true) { const excluded = ['description', 'script'] if (!rootLevel) { diff --git a/src/bin-utils/parser.js b/src/bin-utils/parser.js index b4f1878..551aac9 100644 --- a/src/bin-utils/parser.js +++ b/src/bin-utils/parser.js @@ -5,7 +5,13 @@ import {keyInYN} from 'readline-sync' import {includes, isEqual} from 'lodash' import {oneLine} from 'common-tags' import getLogger from '../get-logger' -import {preloadModule, loadConfig, initialize, help} from '../bin-utils' +import { + preloadModule, + loadConfig, + initialize, + help, + specificHelpScript, +} from '../bin-utils' import getCompletionScripts from './autocomplete-get-scripts' const log = getLogger() @@ -112,15 +118,22 @@ function parse(rawArgv) { return true } const hasDefaultScript = Boolean(psConfig.scripts.default) - const noScriptSpecifiedAndNoDefault = !specifiedScripts.length && - !hasDefaultScript + const noScriptSpecifiedAndNoDefault = + !specifiedScripts.length && !hasDefaultScript const hasHelpScript = Boolean(psConfig.scripts.help) - const commandIsHelp = isEqual(specifiedScripts, ['help']) && !hasHelpScript - if (commandIsHelp || noScriptSpecifiedAndNoDefault) { + const commandIsHelp = + isEqual(specifiedScripts[0], 'help') && !hasHelpScript + const shouldShowSpecificScriptHelp = + commandIsHelp && specifiedScripts.length > 1 + if (shouldShowSpecificScriptHelp) { + log.info(specificHelpScript(psConfig, specifiedScripts[1])) + return true + } else if (commandIsHelp || noScriptSpecifiedAndNoDefault) { parser.showHelp('log') log.info(help(psConfig)) return true } + return false } @@ -220,8 +233,9 @@ function parse(rawArgv) { if (config) { return config } - return findUp.sync('package-scripts.js') || - findUp.sync('package-scripts.yml') + return ( + findUp.sync('package-scripts.js') || findUp.sync('package-scripts.yml') + ) } }