Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"doc",
"infra",
"example",
"video"
"video",
"review"
]
},
{
Expand Down Expand Up @@ -87,7 +88,8 @@
"contributions": [
"bug",
"code",
"test"
"test",
"review"
]
},
{
Expand All @@ -96,7 +98,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/509946?v=3",
"profile": "http://www.hypercubed.com",
"contributions": [
"example"
"example",
"review"
]
},
{
Expand All @@ -108,6 +111,42 @@
"code",
"test"
]
},
{
"login": "boneskull",
"name": "Christopher Hiller",
"avatar_url": "https://avatars.githubusercontent.com/u/924465?v=3",
"profile": "https://boneskull.com",
"contributions": [
"review"
]
},
{
"login": "RobinMalfait",
"name": "Robin Malfait",
"avatar_url": "https://avatars.githubusercontent.com/u/1834413?v=3",
"profile": "robinmalfait.com",
"contributions": [
"example"
]
},
{
"login": "edm00se",
"name": "Eric McCormick",
"avatar_url": "https://avatars.githubusercontent.com/u/622118?v=3",
"profile": "https://ericmccormick.io",
"contributions": [
"review"
]
},
{
"login": "SamVerschueren",
"name": "Sam Verschueren",
"avatar_url": "https://avatars.githubusercontent.com/u/1913805?v=3",
"profile": "https://twitter.com/SamVerschueren",
"contributions": [
"review"
]
}
]
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ All the benefits of npm scripts without the cost of a bloated package.json and l
[![downloads][downloads-badge]][npm-stat]
[![MIT License][license-badge]][LICENSE]

[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors)
[![PRs Welcome][prs-badge]][prs]
[![Donate][donate-badge]][donate]
[![Code of Conduct][coc-badge]][coc]
Expand Down Expand Up @@ -385,9 +385,9 @@ benefits of npm scripts I wanted to keep).
Thanks goes to these people ([emoji key][emojis]):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](http://kent.doddsfamily.us)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) 🚇 💡 📹 | [<img src="https://avatars.githubusercontent.com/u/532272?v=3" width="100px;"/><br /><sub>David Wells</sub>](http://davidwells.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=DavidWells) | [<img src="https://avatars.githubusercontent.com/u/802242?v=3" width="100px;"/><br /><sub>Abhishek Shende</sub>](https://twitter.com/abhishekisnot)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) [⚠️](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) | [<img src="https://avatars.githubusercontent.com/u/185649?v=3" width="100px;"/><br /><sub>Rowan Oulton</sub>](http://travelog.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [📖](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [⚠️](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) | [<img src="https://avatars.githubusercontent.com/u/1915716?v=3" width="100px;"/><br /><sub>Gilad Goldberg</sub>](https://github.com/giladgo)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=giladgo) | [<img src="https://avatars.githubusercontent.com/u/14267457?v=3" width="100px;"/><br /><sub>Tim McGee</sub>](https://github.com/tim-mcgee)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) [📖](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) | [<img src="https://avatars.githubusercontent.com/u/175264?v=3" width="100px;"/><br /><sub>Nik Butenko</sub>](http://butenko.me)<br />💡 [💻](https://github.com/kentcdodds/p-s/commits?author=nkbt) |
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](http://kent.doddsfamily.us)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) 🚇 💡 📹 👀 | [<img src="https://avatars.githubusercontent.com/u/532272?v=3" width="100px;"/><br /><sub>David Wells</sub>](http://davidwells.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=DavidWells) | [<img src="https://avatars.githubusercontent.com/u/802242?v=3" width="100px;"/><br /><sub>Abhishek Shende</sub>](https://twitter.com/abhishekisnot)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) [⚠️](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) | [<img src="https://avatars.githubusercontent.com/u/185649?v=3" width="100px;"/><br /><sub>Rowan Oulton</sub>](http://travelog.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [📖](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [⚠️](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) | [<img src="https://avatars.githubusercontent.com/u/1915716?v=3" width="100px;"/><br /><sub>Gilad Goldberg</sub>](https://github.com/giladgo)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=giladgo) | [<img src="https://avatars.githubusercontent.com/u/14267457?v=3" width="100px;"/><br /><sub>Tim McGee</sub>](https://github.com/tim-mcgee)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) [📖](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) | [<img src="https://avatars.githubusercontent.com/u/175264?v=3" width="100px;"/><br /><sub>Nik Butenko</sub>](http://butenko.me)<br />💡 [💻](https://github.com/kentcdodds/p-s/commits?author=nkbt) |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars.githubusercontent.com/u/1972567?v=3" width="100px;"/><br /><sub>Tommy</sub>](http://www.tommyleunen.com)<br />[🐛](https://github.com/kentcdodds/p-s/issues?q=author%3Atleunen) [💻](https://github.com/kentcdodds/p-s/commits?author=tleunen) [⚠️](https://github.com/kentcdodds/p-s/commits?author=tleunen) | [<img src="https://avatars.githubusercontent.com/u/509946?v=3" width="100px;"/><br /><sub>Jayson Harshbarger</sub>](http://www.hypercubed.com)<br />💡 | [<img src="https://avatars.githubusercontent.com/u/1355481?v=3" width="100px;"/><br /><sub>JD Isaacks</sub>](http://www.jisaacks.com)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=jisaacks) [⚠️](https://github.com/kentcdodds/p-s/commits?author=jisaacks) |
| [<img src="https://avatars.githubusercontent.com/u/1972567?v=3" width="100px;"/><br /><sub>Tommy</sub>](http://www.tommyleunen.com)<br />[🐛](https://github.com/kentcdodds/p-s/issues?q=author%3Atleunen) [💻](https://github.com/kentcdodds/p-s/commits?author=tleunen) [⚠️](https://github.com/kentcdodds/p-s/commits?author=tleunen) 👀 | [<img src="https://avatars.githubusercontent.com/u/509946?v=3" width="100px;"/><br /><sub>Jayson Harshbarger</sub>](http://www.hypercubed.com)<br />💡 👀 | [<img src="https://avatars.githubusercontent.com/u/1355481?v=3" width="100px;"/><br /><sub>JD Isaacks</sub>](http://www.jisaacks.com)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=jisaacks) [⚠️](https://github.com/kentcdodds/p-s/commits?author=jisaacks) | [<img src="https://avatars.githubusercontent.com/u/924465?v=3" width="100px;"/><br /><sub>Christopher Hiller</sub>](https://boneskull.com)<br />👀 | [<img src="https://avatars.githubusercontent.com/u/1834413?v=3" width="100px;"/><br /><sub>Robin Malfait</sub>](robinmalfait.com)<br />💡 | [<img src="https://avatars.githubusercontent.com/u/622118?v=3" width="100px;"/><br /><sub>Eric McCormick</sub>](https://ericmccormick.io)<br />👀 | [<img src="https://avatars.githubusercontent.com/u/1913805?v=3" width="100px;"/><br /><sub>Sam Verschueren</sub>](https://twitter.com/SamVerschueren)<br />👀 |
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
18 changes: 18 additions & 0 deletions other/ERRORS_AND_WARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,21 @@ This happens when you use the `--require` flag and the module you specify cannot

1. Check that you spelled the module correctly
2. Check that the module you wish to require is require-able

## Failed with exit code

This means that one of the scripts p-s tried to run resulted in a non-zero exit code (a failing exit code)

### To Fix:

Try to run the script without `p-s` and verify that the script is working. If not, fix that. If it's working without `p-s` it could be a problem with `p-s`. Please file an issue.

## Emitted an error

This means that the child process for the specified script emitted an error.

### To Fix:

Look at the error and try to figure out why the script would be failing.

Try to run the script without `p-s` and verify that the script is working. If not, fix that. If it's working without `p-s` it could be a problem with `p-s`. Please file an issue.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"license": "MIT",
"dependencies": {
"arrify": "1.0.1",
"async": "2.0.1",
"bluebird": "3.4.6",
"colors": "1.1.2",
"commander": "2.9.0",
"find-up": "1.1.2",
Expand All @@ -30,7 +30,7 @@
"omelette": "0.3.1",
"prefix-matches": "0.0.9",
"shell-escape": "0.2.0",
"spawn-command": "0.0.2"
"spawn-command-with-kill": "1.0.0"
},
"devDependencies": {
"all-contributors-cli": "3.0.6",
Expand Down
9 changes: 3 additions & 6 deletions src/bin/p-s.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,9 @@ function loadAndRun(scriptsAndArgs, psConfig) {
parallel: scriptsAndArgs.parallel,
logLevel: program.logLevel,
}),
}, result => {
if (result.error) {
log.error(result.error)
process.exit(FAIL_CODE)
}
process.exit(result.code)
}).catch(error => {
log.error(error)
process.exitCode = error.code || FAIL_CODE
})
}

Expand Down
7 changes: 6 additions & 1 deletion src/get-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ function getLogger(logLevel) {

function getMessage(first, ...rest) {
if (isPlainObject(first) && first.message && first.ref) {
return [...arrify(first.message), getLink(first.ref), ...rest]
return [
...arrify(first.message),
getLink(first.ref),
first.error,
...rest,
].filter(i => !!i)
} else {
return [first, ...rest]
}
Expand Down
116 changes: 92 additions & 24 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,125 @@
import spawn from 'spawn-command'
import async from 'async'
import spawn from 'spawn-command-with-kill'
import Promise from 'bluebird'
import colors from 'colors/safe'
import {isString, find, clone} from 'lodash'
import {isString, clone} from 'lodash'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full lodash is quite huge, why not install them separately?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get you, but honestly I'm using about a dozen methods and I'd rather avoid installing each separately/managing them. It's 1.4MB (uncompressed) and the likelihood that someone's already depending on lodash in their project (even if it's transitively) is quite high.

Thanks for the suggestion.

import {sync as findUpSync} from 'find-up'
import managePath from 'manage-path'
import arrify from 'arrify'
import getScriptToRun from './get-script-to-run'
import getScriptsFromConfig from './get-scripts-from-config'
import getLogger from './get-logger'

const noop = () => {} // eslint-disable-line func-style, no-empty-function
const NON_ERROR = 0

export default runPackageScripts

function runPackageScripts({scriptConfig, scripts, args, options = {}}, callback = noop) {
function runPackageScripts({scriptConfig, scripts, args, options = {}}) {
if (scripts.length === 0) {
scripts = ['default']
}
const scriptNames = arrify(scripts)
const method = options.parallel ? 'map' : 'mapSeries'
async[method](scriptNames, (scriptName, cb) => {
const child = runPackageScript({scriptConfig, options, scriptName, args})
if (child.on) {
child.on('exit', exitCode => cb(null, exitCode))
} else {
cb(child)
}
}, (err, results) => {
if (err) {
callback({error: err})
} else {
const NON_ERROR = 0
const result = find(results, r => r !== NON_ERROR)
callback({code: result})
if (options.parallel) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the code below could be greatly simplified with RxJS. But I have no idea how to use Observables. Someone please help me!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see an opportunity for observables. Might be missing something though. Why do you think observables make sense here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never used Observables, but I thought that they're supposed to really help with complex async stuff. 🤷

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be done a little bit cleaner though. I'll do a PR later today or this weekend.

Copy link

@ta2edchimp ta2edchimp Sep 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe zip is what you're looking for (re RxJS)? Merge multiple Observables/Promises, subscribe to the result and have a single handling of their results ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what I'm looking for.. I just think this code could be cleaned up a bit...

Copy link

@SamVerschueren SamVerschueren Sep 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When running in parallel, you can just use Promise.all, when executing serially you can do something like this.

const result = promises.reduce((res, task) => {
   return res.then(() => task());
}, Promise.resolve());

result.then(...);

return runParallel()
} else {
return runSeries()
}

function runSeries() {
return scriptNames.reduce((res, scriptName) => {
return res.then(() => (
runPackageScript({scriptConfig, options, scriptName, args})
))
}, Promise.resolve())
}

function runParallel() {
const results = scriptNames.map(script => ({script, code: undefined}))
let aborted = false

const promises = scriptNames.map(scriptName => {
return runPackageScript({scriptConfig, options, scriptName, args})
})

const allPromise = Promise.all(promises.map((promise, index) => {
return promise.then(code => {
if (!aborted) {
results[index].code = code
}
})
})).then(() => results)

allPromise.catch(() => {
/* istanbul ignore if */
if (aborted) {
// this is very unlikely to happen
} else {
abortAll()
}
})

return allPromise

function abortAll() {
aborted = true
promises.forEach(p => p.abort())
}
})
}
}


function runPackageScript({scriptConfig, options, scriptName, args}) {
const scripts = getScriptsFromConfig(scriptConfig, scriptName)
const script = getScriptToRun(scripts, scriptName)
if (!isString(script)) {
return {
return Promise.reject({
message: colors.red(
`Scripts must resolve to strings. There is no script that can be resolved from "${scriptName}"`
),
ref: 'missing-script',
}
})
}
const command = [script, args].join(' ').trim()
const log = getLogger(getLogLevel(options))
log.info(colors.gray('p-s executing: ') + colors.green(command))
return spawn(command, {stdio: 'inherit', env: getEnv()})
let child
const promise = new Promise((resolve, reject) => {
child = spawn(command, {stdio: 'inherit', env: getEnv()})

child.on('error', error => {
child = null
reject({
message: colors.red(
`The script called "${scriptName}" which runs "${command}" emitted an error`
),
ref: 'emitted-an-error',
error,
})
})

child.on('close', code => {
child = null
if (code === NON_ERROR) {
resolve(code)
} else {
reject({
message: colors.red(
`The script called "${scriptName}" which runs "${command}" failed with exit code ${code}`
),
ref: 'failed-with-exit-code',
code,
})
}
})
})

promise.abort = function abort() {
if (child !== null) {
child.kill()
child = null
}
}

return promise
}

function getLogLevel({silent, logLevel}) {
Expand Down
Loading