Skip to content

Commit

Permalink
feat(cli): Added util to display help for a specific script (#151)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
imbhargav5 authored and Kent C. Dodds committed Jul 17, 2017
1 parent c0c1704 commit 9d695d8
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 20 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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!
Expand Down
65 changes: 64 additions & 1 deletion src/bin-utils/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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)
}
29 changes: 29 additions & 0 deletions src/bin-utils/__tests__/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jest.mock('../../bin-utils', () => {
}
}),
help: jest.fn(),
specificHelpScript: jest.fn(),
clearAll,
mock: {},
}
Expand Down Expand Up @@ -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
Expand Down
44 changes: 32 additions & 12 deletions src/bin-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
isUndefined,
isEmpty,
isFunction,
isNull,
} from 'lodash'
import typeOf from 'type-detect'
import chalk from 'chalk'
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()
Expand Down Expand Up @@ -100,7 +102,14 @@ function loadConfig(configPath, input) {
return config
}

export {initialize, help, getModuleRequirePath, preloadModule, loadConfig}
export {
initialize,
help,
getModuleRequirePath,
preloadModule,
loadConfig,
specificHelpScript,
}

/****** implementations ******/

Expand Down Expand Up @@ -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')}`
Expand All @@ -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) {
Expand Down
28 changes: 21 additions & 7 deletions src/bin-utils/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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')
)
}
}

Expand Down

0 comments on commit 9d695d8

Please sign in to comment.