forked from arshaw/shelljs-live
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3944596
Showing
11 changed files
with
734 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# http://editorconfig.org | ||
|
||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true | ||
quote_type = single | ||
trim_semicolon = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: CI | ||
on: [push] | ||
jobs: | ||
CI: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
- name: Setup Node | ||
uses: actions/setup-node@v2 | ||
with: | ||
node-version: "14" | ||
- name: Restore Dependencies | ||
uses: actions/cache@v2 | ||
id: node-cache | ||
with: | ||
# https://dev.to/mpocock1/how-to-cache-nodemodules-in-github-actions-with-yarn-24eh | ||
path: '**/node_modules' | ||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} | ||
- name: Install Dependencies | ||
run: yarn install | ||
- name: Build Project | ||
run: yarn run build | ||
- name: Run Tests | ||
run: yarn run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
|
||
# shelljs-live | ||
|
||
Execute a shell command while piping output directly to your console. | ||
|
||
Motivated by [shelljs] exec's [inability to preserve colors](https://github.com/shelljs/shelljs/issues/86). | ||
Also, great for watcher tasks, as output is not buffered. | ||
|
||
Very portable (uses [cross-spawn]), especially when specifying `command` as an array of strings (more below). | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install shelljs shelljs-live | ||
``` | ||
|
||
The `shelljs` package is a peerDependency of `shelljs-live` and must be installed. | ||
|
||
## Traditional API | ||
|
||
``` | ||
live(command [, options] [, callback]) => statusCode | ||
``` | ||
|
||
- `command` - A string **OR** an array of strings: | ||
- If a string, run as a shell statement. The shell might expand globs or have opinions about escaping. This is not very portable. | ||
- If an *array* of strings, all arguments are piped directly to a command and no escaping is necessary. **This is recommended.** | ||
- `options` - *Optional*. [More info](#options). | ||
- `callback` - *Optional*. Called on success/failure. Receives the `statusCode`. Implies the `async:true` option. | ||
- `statusCode` - A number, or in some cases `null`. Success means `statusCode === 0`. | ||
|
||
Synchronous usage: | ||
|
||
```js | ||
const { live } = require('shelljs-live') | ||
|
||
const statusCode = live(['ps', '-ax']) // live('ps -ax') works too. not recommended | ||
if (statusCode === 0) { | ||
console.log('Success') | ||
} else { | ||
console.log('Failure') | ||
} | ||
``` | ||
|
||
Asynchronous usage: | ||
|
||
```js | ||
const { live } = require('shelljs-live') | ||
|
||
live(['ps', '-ax'], (statusCode) => { | ||
if (statusCode === 0) { | ||
console.log('Success') | ||
} else { | ||
console.log('Failure') | ||
} | ||
}) | ||
``` | ||
|
||
## Promise API | ||
|
||
``` | ||
live(command [, options]) => promise | ||
``` | ||
|
||
- `command`: A string **OR** an array of strings: | ||
- If a string, run as a shell statement. The shell might expand globs or have opinions about escaping. This is not very portable. | ||
- If an *array* of strings, all arguments are piped directly to a command and no escaping is necessary. **This is recommended.** | ||
- `options`: *Optional*. [More info](#options). | ||
- `promise`: A [Promise] that triggers success when status code equals `0`, failure otherwise. Neither handler receives the status code. | ||
|
||
Usage: | ||
|
||
```js | ||
const { live } = require('shelljs-live/promise') | ||
|
||
live(['ps', '-ax']).then(() => { | ||
console.log('Success') | ||
}, () => { | ||
console.log('Failure') | ||
}) | ||
``` | ||
|
||
Or if you want to use `await` and don't care about handling errors: | ||
|
||
```js | ||
const { live } = require('shelljs-live/promise') | ||
|
||
await live(['ps', '-ax']) | ||
console.log('Success') | ||
``` | ||
|
||
## Options | ||
|
||
- `async`: Asynchronous execution. If a callback is provided, or using the Promise API, it will be set to true, regardless of the passed value (default: `false`). | ||
- `fatal`: Exit upon error (default: `false`, inherits from [ShellJS Config][shelljs-config]). | ||
- `silent`: Do not echo program output to console (default: `false`, inherits from [ShellJS Config][shelljs-config]). | ||
|
||
Any other option, such as `cwd`, is passed directly to [spawn]. | ||
|
||
|
||
[shelljs]: https://documentup.com/shelljs/shelljs | ||
[shelljs-config]: https://documentup.com/shelljs/shelljs#configuration | ||
[cross-spawn]: https://www.npmjs.com/package/cross-spawn | ||
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise | ||
[spawn]: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "shelljs-live", | ||
"version": "0.0.1", | ||
"repository": "https://github.com/arshaw/shelljs-live.git", | ||
"author": "Adam Shaw", | ||
"license": "MIT", | ||
"main": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"exports": { | ||
".": "./dist/index.js", | ||
"./promise": "./dist/promise.js" | ||
}, | ||
"scripts": { | ||
"clean": "rm -rf dist", | ||
"build": "tsc", | ||
"watch": "tsc --watch", | ||
"test": "./test.sh" | ||
}, | ||
"peerDependencies": { | ||
"shelljs": "^0.8.4" | ||
}, | ||
"dependencies": { | ||
"cross-spawn": "^7.0.3" | ||
}, | ||
"devDependencies": { | ||
"@types/cross-spawn": "^6.0.2", | ||
"@types/shelljs": "^0.8.9", | ||
"shelljs": "^0.8.4", | ||
"typescript": "^4.4.3", | ||
"yargs": "^17.1.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { SpawnOptions } from 'child_process' | ||
import * as spawn from 'cross-spawn' | ||
import { config } from 'shelljs' | ||
|
||
export type Callback = (status: number | null) => void | ||
export type Options = SpawnOptions & { async?: boolean, fatal?: boolean, silent?: boolean } | ||
|
||
export function live(command: string | string[], options?: Options): number | null | ||
export function live(command: string | string[], callback: Callback): number | null | ||
export function live( | ||
command: string | string[], | ||
options: Options | undefined, | ||
callback: Callback, | ||
): number | null | ||
export function live( | ||
command: string | string[], | ||
optionsOrCallback?: Options | Callback, | ||
callback?: Callback, | ||
): number | null { | ||
let options: Options | ||
|
||
if (typeof optionsOrCallback === 'function') { | ||
callback = optionsOrCallback | ||
options = {} | ||
} else { | ||
options = optionsOrCallback || {} | ||
} | ||
|
||
let command0: string | ||
let args: string[] | ||
let shell: boolean | ||
|
||
if (Array.isArray(command)) { | ||
command0 = command[0] | ||
args = command.slice(1) | ||
shell = false | ||
} else { | ||
command0 = command | ||
args = [] | ||
shell = true | ||
} | ||
|
||
if (!command0) { | ||
throw new Error('Must specify a command') | ||
} | ||
|
||
const fatal = options.fatal ?? config.fatal | ||
const silent = options.silent ?? config.silent | ||
const spawnOptions: SpawnOptions = { | ||
...(silent ? {} : { stdio: 'inherit' }), | ||
...options, | ||
shell, | ||
} | ||
|
||
function handleStatus(status: number | null) { | ||
if (status === null || status !== 0) { | ||
if (fatal) { | ||
console.error(`Command '${command0}' failed with status code ${status}`) | ||
process.exit(status || 1) | ||
} | ||
} | ||
if (callback) { | ||
callback(status) | ||
} | ||
} | ||
|
||
if (options.async || callback) { | ||
const childProcess = spawn(command0, args, spawnOptions) | ||
childProcess.on('close', handleStatus) | ||
return null | ||
} else { | ||
const { status } = spawn.sync(command0, args, spawnOptions) | ||
handleStatus(status) | ||
return status | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { live as origLive, Options } from './' | ||
|
||
export function live(tokens: string[], options?: Options): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
origLive(tokens, options, (status) => { | ||
if (status === 0) { | ||
resolve() | ||
} else { | ||
reject(new Error(`Command '${tokens[0]}' failed with status code ${status}`)) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
export { Options } from './' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
const yargs = require('yargs/yargs') | ||
const { hideBin } = require('yargs/helpers') | ||
const shell = require('shelljs') | ||
const { live } = require('./dist/index') | ||
const { live: promiseLive } = require('./dist/promise') | ||
|
||
const argv = yargs(hideBin(process.argv)).argv | ||
shell.config.silent = Boolean(argv.silent) | ||
shell.config.fatal = Boolean(argv.fatal) | ||
|
||
// TODO: test silent/fatal passed-in as options | ||
|
||
let successStatusA = live(argv.shell ? 'ls -al && cd .' : ['ls', '-al']) | ||
if (successStatusA !== 0) { | ||
console.error('should succeed') | ||
process.exit(successStatusA) | ||
} | ||
|
||
let successStatusB = live(argv.shell ? 'ls -al && cd .' : ['ls', '-al'], { | ||
cwd: argv.cwd || '.' | ||
}) | ||
if (successStatusB !== 0) { | ||
console.error('should succeed in different CWD') | ||
process.exit(successStatusB) | ||
} | ||
|
||
let failureStatusA = live(['asdfasdfasdf']) | ||
if (failureStatusA === 0) { | ||
console.error('status should be non-zero when command does not exist') | ||
process.exit(1) | ||
} | ||
|
||
let failureStatusB = live(['ls', 'asdfasdfasdf']) | ||
if (failureStatusB === 0) { | ||
console.error('status should be non-zero when command fails') | ||
process.exit(1) | ||
} | ||
|
||
function testSuccessAsync() { | ||
let start = Date.now() | ||
return new Promise((resolve, reject) => { | ||
live(['sleep', '1'], (status) => { | ||
let end = Date.now() | ||
let dur = end - start | ||
if (status !== 0) { | ||
reject(new Error('async did not succeed')) | ||
} else if (dur < 1000) { | ||
reject(new Error('async is not async')) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
function testSuccessPromise() { | ||
let start = Date.now() | ||
return promiseLive(['sleep', '1']).then(() => { | ||
let end = Date.now() | ||
let dur = end - start | ||
if (dur < 1000) { | ||
throw new new Error('promise is not async') | ||
} | ||
}, () => { | ||
throw new Error('promise did not fail') | ||
}) | ||
} | ||
|
||
function testFailurePromise() { | ||
return promiseLive(['ls', 'asdfasdfasdf']).then(() => { | ||
throw new Error('async should fail') | ||
}, () => { | ||
// handle error by doing nothing | ||
}) | ||
} | ||
|
||
Promise.all([ | ||
testSuccessAsync(), | ||
testSuccessPromise(), | ||
testFailurePromise(), | ||
]).then(() => { | ||
if (!argv.silent) { | ||
console.log('Successfully ran all tests') | ||
} | ||
}, (err) => { | ||
console.error(err.toString()) | ||
process.exit(1) | ||
}) |
Oops, something went wrong.