Skip to content

Commit

Permalink
Merge 272d175 into f01ab0d
Browse files Browse the repository at this point in the history
  • Loading branch information
Kent C. Dodds authored Jul 29, 2016
2 parents f01ab0d + 272d175 commit 8d17061
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 8 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,14 @@ npm install --global p-s
```

From here you can use `p-s` on the command line via one of the installed aliases: `p-s`, `package-scripts`, or `nps`.
In the future, this may support autocomplete capabilities. Check out #28 for updates on this.
If you do this, you may also be interested in installing the shell autocompletion script. Do so by running:

```
nps completion <optionally-your-bash-profile-file>
```

The bash profile file defaults to `~/.bash_profile`. Special thanks to the [`omelette`][omelette] package for making
this so easy.

## Getting started

Expand Down Expand Up @@ -392,3 +399,4 @@ MIT
[scripty]: https://npmjs.com/package/scripty
[npm scripts]: https://docs.npmjs.com/misc/scripts
[video]: http://kcd.im/p-s-video
[omelette]: https://npmjs.com/package/omelette
12 changes: 10 additions & 2 deletions package-scripts.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/* eslint prefer-template:"off", no-var:"off", max-len:[2, 200] */ // this file runs in node 0.10.0
var transpile = 'babel --copy-files --out-dir dist --ignore *.test.js,fixtures src'

var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1])
var validate = ['build', 'test']
if (nodeVersion >= 4) {
validate.push('lint') // we can't run linting on node versions < 4
}

module.exports = {
scripts: {
commit: {
Expand All @@ -21,8 +24,13 @@ module.exports = {
},
},
build: {
description: 'deletes the `dist` directory and transpiles all relevant `src` to the `dist`',
script: 'rimraf dist && babel --copy-files --out-dir dist --ignore *.test.js,fixtures src',
default: {
description: 'deletes the `dist` directory and transpiles all relevant `src` to the `dist`',
script: 'rimraf dist && ' + transpile,
},
watch: {
script: 'rimraf dist && ' + transpile + ' --watch',
},
},
lint: {
description: 'lint the code with eslint',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"find-up": "1.1.2",
"lodash": "4.14.0",
"manage-path": "2.0.0",
"omelette": "0.3.1",
"prefix-matches": "0.0.9",
"shell-escape": "0.2.0",
"spawn-command": "0.0.2"
Expand Down
38 changes: 38 additions & 0 deletions src/bin-utils/autocomplete/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint no-invalid-this:"off" */
import omelette from 'omelette'
import {includes, isPlainObject, kebabCase} from 'lodash'

const complete = omelette('nps <script>')

export {autocomplete as default, install}

function autocomplete(config = {}) {
complete.on('script', onScript)
complete.init()

function onScript() {
this.reply(getScripts(config.scripts))
}
}

function install(destination = '~/.bash_profile') {
complete.setupShellInitFile(destination)
return destination
}

function getScripts(scriptsObject, prefix = '', allScripts = []) {
const excludedKeys = ['default', 'script', 'description']
return Object.keys(scriptsObject).reduce((acc, key) => {
if (includes(excludedKeys, key)) {
return acc
}
const value = scriptsObject[key]
const kebabKey = kebabCase(key)
const deepKey = prefix ? `${prefix}.${kebabKey}` : kebabKey
acc.push(deepKey)
if (isPlainObject(value)) {
getScripts(value, deepKey, acc)
}
return acc
}, allScripts)
}
73 changes: 73 additions & 0 deletions src/bin-utils/autocomplete/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import test from 'ava'
import {spy} from 'sinon'
import proxyquire from 'proxyquire'

test('inits omelette', t => {
const {autocomplete, initSpy} = getAutocomplete()
autocomplete()
t.true(initSpy.calledOnce)
})

test('calls this.reply with the available scripts', t => {
const stubConfig = {
scripts: {
build: {default: {script: 'build'}, watch: 'build.watch', main: {umd: 'build.main.umd', default: 'build.main'}},
lint: {default: {script: 'lint', description: 'lint things'}, watch: 'lint.watch'},
test: 'test',
camelCase: 'camelCase',
cover: {description: 'this is a description', script: 'this is the script'},
},
}
const expectedReplyArgs = [
'build', 'build.watch', 'build.main', 'build.main.umd',
'lint', 'lint.watch',
'test', 'camel-case', 'cover',
]
const {autocomplete, getReplyArgs} = getAutocomplete()
autocomplete(stubConfig)
const actualReplyArgs = getReplyArgs()
t.deepEqual(actualReplyArgs, expectedReplyArgs)
})

test('install calls setupShellInitFile with the given destination', t => {
const {install, setupShellInitFileSpy} = getInstall()
const destination = '~/.my_bash_profile'
install(destination)
const [actualDestination] = setupShellInitFileSpy.firstCall.args
t.true(setupShellInitFileSpy.calledOnce)
t.is(actualDestination, destination)
})

test('install defaults to ~/.bash_profile', t => {
const {install, setupShellInitFileSpy} = getInstall()
const expectedDestination = '~/.bash_profile'
install()
const [actualDestination] = setupShellInitFileSpy.firstCall.args
t.true(setupShellInitFileSpy.calledOnce)
t.is(actualDestination, expectedDestination)
})

function getAutocomplete() {
const onSpy = spy()
const initSpy = spy()
const omelette = spy(() => {
return {on: onSpy, init: initSpy}
})
const autocomplete = proxyquire('./index', {omelette}).default
return {autocomplete, getReplyArgs, initSpy}

function getReplyArgs() {
const [, replier] = onSpy.firstCall.args
const reply = spy()
const context = {reply}
replier.call(context)
return reply.firstCall.args[0]
}
}

function getInstall() {
const setupShellInitFileSpy = spy()
const omelette = spy(() => ({setupShellInitFile: setupShellInitFileSpy}))
const {install} = proxyquire('./index', {omelette})
return {install, setupShellInitFileSpy}
}
3 changes: 2 additions & 1 deletion src/bin-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import colors from 'colors/safe'
import getLogger from '../get-logger'
import {resolveScriptObjectToScript} from '../resolve-script-object-to-string'
import initialize from './initialize'
import {default as autocomplete, install as installAutocomplete} from './autocomplete'

const log = getLogger()

Expand Down Expand Up @@ -36,7 +37,7 @@ const loadConfig = getAttemptModuleRequireFn(function onFail(configPath, require
})

export {
getScriptsAndArgs, initialize, help,
getScriptsAndArgs, initialize, help, autocomplete, installAutocomplete,
getModuleRequirePath, preloadModule, loadConfig,
}

Expand Down
33 changes: 29 additions & 4 deletions src/bin/p-s.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
#!/usr/bin/env node
/* eslint no-process-exit: "off" */
import findUp from 'find-up'
import {merge} from 'lodash'
import {merge, includes} from 'lodash'
import program from 'commander'
import colors from 'colors/safe'
import runPackageScript from '../index'
import {getScriptsAndArgs, initialize, help, preloadModule, loadConfig} from '../bin-utils'
import {
getScriptsAndArgs, initialize, autocomplete, installAutocomplete,
help, preloadModule, loadConfig,
} from '../bin-utils'
import getLogger from '../get-logger'

const {version} = require('../../package.json')

const log = getLogger()
const FAIL_CODE = 1
let shouldRun = true
const shouldAutocomplete = includes(process.argv, '--compbash')

program
.version(require('../../package.json').version)
.version(version)
.allowUnknownOption()
.option('-s, --silent', 'Silent p-s output')
.option('-p, --parallel <script-name1,script-name2>', 'Scripts to run in parallel (comma seprated)')
.option('-c, --config <filepath>', 'Config file to use (defaults to nearest package-scripts.js)')
.option('-l, --log-level <level>', 'The log level to use (error, warn, info [default])')
.option('-r, --require <module>', 'Module to preload')
.on('init', onInit)
.on('completion', onRequestToInstallCompletion)
.on('--help', onHelp)
.parse(process.argv)

if (shouldRun) {

if (shouldAutocomplete) {
autocomplete(getPSConfig())
} else if (shouldRun) {
const psConfig = getPSConfig()
const hasDefaultScript = !!psConfig.scripts.default
const scriptsAndArgs = getScriptsAndArgs(program)
Expand Down Expand Up @@ -73,9 +83,24 @@ function onInit() {
'Check out your scripts in there. Go ahead and update them and add descriptions to the ones that need it'
))
log.info(colors.gray('Your package.json scripts have also been updated. Run `npm start -- --help` for help'))
log.info(colors.gray(
'You may also want to install the package globally and installing autocomplete script. You can do so by running\n' +
' npm install --global p-s\n' +
' nps completion <optionally-your-bash-profile-file>\n' +
'The bash profile file defaults to ~/.bash_profile'
))
}

function onHelp() {
shouldRun = false
log.info(help(getPSConfig()))
}

function onRequestToInstallCompletion() {
shouldRun = false
const [, bin,, destination] = process.argv
const finalDestination = installAutocomplete(destination)
log.info(
`Autocompletion has been set up and installed into ${colors.bold.green(finalDestination)} for ${colors.bold(bin)}`,
)
}

0 comments on commit 8d17061

Please sign in to comment.