npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@getvim/execute

v1.0.0

Published

Extremely opinionated Bash execution from Node.js

Downloads

7,703

Readme

@getvim/execute

This is an extremely opinionated, zero dependencies, tiny module for executing command line programs from Node.js using a Bash shell.

The design philosophy behind this module aims to abstract away many of the Bash pitfalls most of us never knew existed.

Requirements

You must be working in an environment where /bin/bash is available.

Minimal Node.js version is 8.0.0.

Usage

Installation

npm install --save @getvim/execute
yarn add @getvim/execute

Basic usage

const { execute } = require('@getvim/execute');
execute('ls')
    .then(console.log); // result: "feature.js\nfeature.spec.js"

Environment variables

const { execute } = require('@getvim/execute');
execute('echo $MY_NAME', { env: { MY_NAME: 'Jake Chambers' } })
    .then(console.log); // result: "Jake Chambers"

Piping

Piping allows you to interleave shell commands with modern JS features that are safer and easier to use than a lot of shell alternatives like using external modules, concurrency, loops or string formatters.

const { execute, pipe } = require('@getvim/execute');
const mailer = require('@fictional/mail-client')
execute('ls | grep temp')
    .then(tempFiles => {
        mailer('[email protected]', 'Temp files being deleted', tempFiles);
        return tempFiles;
    })
    .then(pipe('xargs rm'));

Errors

const { execute } = require('@getvim/execute');
execute('npm install')
    .then(() => execute('npm test')) // Has a failing test
    .then(() => execute('npm publish'))
    .catch(error => console.error('Failed CI/CD!', error));
    /* result:
        Failed CI/CD! 
        { 
            killed: false, 
            code: 1, 
            signal: null, 
            cmd: "set -euo pipefail\nnpm test" 
            stdout: "... test results ..."
            stderr: "... failing test ..."
        } 
    */

Behavior

  • execute's Promise will reject in the following cases:
    • Usage of an undefined environment variable
    • A command, in a pipe or otherwise, returned an exit code other than 0 (even greping without a result will do this)
  • Results are always a string.
  • Processes are not interactive and do not print to the screen. If a process is waiting for user input, (like a prompt for a Y/N confirmation) the Promise will never resolve/reject.

Why?

Bash has a lot of pitfalls. As code organizations grow their reliance on CI/CD scripts or internal tooling for day-to-day work grow as well. Most organizations write those scripts in pure Bash. Most of the people reading/writing these scripts will find themselves victims of those counter intuitive Bash pitfalls. This module aims to take some very opinionated decisions that will guard its users from 80% of these common mistakes.

Every problem that this tool purports to solve can be solved using Bash only solutions, but it requires learning time most developers will not invest. Using this module you can use language features you're already familiar with instead of learning the archaic and unintuitive Bash solutions.

Common Bash Pitfalls

Only the last error counts

Problem:

npm install
npm test # this contains a failing test, the exit code here is nonzero
npm publish # Bash continues to run the script and npm publish will still happen

Solution: Any command run with execute will be run with the set -e flag and the script will immediately exit with a Promise rejection on any line that returns a non zero exit code.

Failure mid-pipe doesn't count

Problem:

failingCommand | echo "a" # failingCommand does not throw an error because it's in a pipe
echo "b"
# result:
# a
# b

Solution: Any command run with execute will be run with the set -o pipefail flag and the script will immediately exit with a Promise rejection on any command in a pipe chain that returns a non zero exit code.

Using unset variables does not throw errors

Problem:

rm -rf $BUILD_FOLDER/* # If $BUILD_FOLDER is unset by mistake, this line would delete the entire filesystem

Solution: Any command run with execute will be run with the set -u flag and the script will immediately exit with a Promise rejection on any usage of an unset environment variable.

Basic built-in language features like string manipulation are easy to get wrong

Problem:

# Trying to extract the Nth word in a string
echo $STRING | awk -v N=$N '{print $N}' # You have to learn awk or other tools

Solution: Usage of execute.pipe allows to easily jump between a Bash context and a JS context.

const { execute, pipe } = require('@getvim/execute');
const N = 5;
execute('echo $STRING', { env: { STRING:'My name is Inigo Montoya'}})
    .then(result => result.split(' ')[N - 1])
    .then(pipe('rev'))
    .then(console.log); // Result: "ayotnoM"

Basic built-in language constructs like IF-ELSE / Conditionals are easy to get wrong

Problem: It very easy to get Syntax error or unintended behavior from Bash's language constructs.

# Checking if a variable is greater than 0
if [$VAR > 0]; then ... # Syntax error, forgot spaces

if [ $VAR > 0 ]; then ... # ">" Means stream redirection

if [[ $VAR > 0 ]]; then ... # ">" Means comparison, but of strings (lexicographs)

if [[ $VAR -gt 0 ]]; then ... # Finally works!

if (( $VAR > 0 )); then ... # Confusingly, this also works...

Solution: Execute allows to easily jump between a Bash context and a JS context.

const { execute, pipe } = require('@getvim/execute');
execute('wc -l file.txt')
    .then(lineCount => lineCount > 0 ? "Success" : "Failure")
    .then(pipe("tee SomeFile.txt"))