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

argster

v1.3.1

Published

A simple command/argument manager

Downloads

13

Readme

argster

A simple command/argument manager with a simple API that makes it easy to build dynamic commands and dynamic arguments with computed values.

What

The Builder's builds and manages commands. When a new Builder instance is created, it provides an simple API to create and execute commands, append and prepend dynamic arguments.

When creating commands through the builder, it goes through the list of file patterns recursively, reads each file, parses each line in the file, resolves dynamic variables to their values, and generates an executable command string. When a command is executed, it returns an object which contains both a promise which is resolved or rejected based on how the process exits (0 / 1), along with a Readable streams object for listening to stdout and stderr streams.

So instead of manually creating the following command in our CI pipeline, with dynamic arguments and dynamic values that need to be computed at runtime:

docker build . -t myimage:1.0.0 --label org.label-schema.build-date=2019-07-14 --label org.label-schema.name=argster-120-example --label org.label-schema.vendor=Vendor --label org.label-schema.version=1.0.0 --label org.label-schema.schema-version=1.0.0-rc.1

You could create a simple JavaScript script and create complex commands with dynamic arguments (evaluated JavaScript functions) and version control it all with Git.

Example (docker build)

Pay special attention to the org.label-schema.build-date label with the dynamic date.

1. If we created the following builder and command

const options = {
  dynamicVariables: {
    BUILD_DATE: () => new Date().toISOString().slice(0, 10),
    NAME: () => pkg.name,
    VERSION: () => pkg.version,
    DESCRIPTION: () => pkg.description,
    VENDOR: () => "Vendor",
    SCHEMA_VERSION: () => "1.0.0-rc.1"
  },
  skipUnresolvedVariables: true
};

const builder = new Builder(options);

const cmd = builder.createCommand('docker build .', [
  {
    patterns: ['**/*.lbl'],
    prefix: '--label'
  }
]);

cmd.prependArgument({
  argument: `myimage:${pkg.version}`,
  prefix: '-t'
});

command.exec();

2. And had this pattern file

./some/deep/path/file.lbl

org.label-schema.build-date=${BUILD_DATE}
org.label-schema.name=${NAME}
org.label-schema.description=${DESCRIPTION}
org.label-schema.vendor=${VENDOR}
org.label-schema.version=${VERSION}
org.label-schema.schema-version=${SCHEMA_VERSION}

3. This will be the executed command:

docker build . -t myimage:1.0.0 --label org.label-schema.build-date=2019-07-14 --label org.label-schema.name=argster-120-example --label org.label-schema.vendor=Vendor --label org.label-schema.version=1.0.0 --label org.label-schema.schema-version=1.0.0-rc.1

Why

I always felt the lack of a tool/library to easily create dynamic commands with dynamic arguments that were derived by functions but had the possibility to version control, without creating complex shell scripts. It first started when I was experimenting with DevOps and started to work with Docker trying to create sane images. I started reading about best practices around labeling and tagging Docker images and stumbled up on Label Schema.

I sat down and thought about the following technologies:

  • Label Schema and label semantics for e.g. Docker images
  • Dockerfile and ENV / ARG
  • Dockerfile and .env file
  • Power of JavaScript
  • Power of variables
  • Power of Git

With the great tech above I wanted to utilise it all and create a Node library that was able to build up complex dynamic command-line commands and arguments. Then the idea of argster was born.

Check out this CodeSandbox for examples.

Checkout the roadmap for what to come.

Setup

You can install the npm package with yarn or npm:

yarn

yarn add argster

npm

npm install argster

Concepts

Builder

A builder is a core component that builds and manages commands. The builder accepts options to adjust how command arguments are parsed, etc.

Command

A command is a string that is executed as a process. It contains a base command and arguments that can be assembled and manipulated. When a command is created, it accepts file patterns as arguments, that are then processed, evaluated and converted to a finalized command string, which is then executed.

File patterns

File patterns are structures that contain a prefix and a list of file patterns, where each file matching the pattern is read and each line assembled into a command string, which is then executed.

Example for docker build:

{
  prefix: '--build-arg',
  patterns: ['**/*.arg']
},
{
  prefix: '--label',
  patterns: ['**/*.lbl']
}

All files matching the glob pattern **/*.arg and **/*.lbl would be read and a command generated from those arguments.

When the command arguments are evaluated, a finalized command string is generated from those file contents.

Builder Options

rootDir

Root directory to resolve all paths from.

Default

process.cwd()

dynamicVariables

Key-Value map containing the name of the variable and its value.

Possible values
[key: string]: string | (() => string);
Example
{
  BUILD_DATE: () => new Date().toISOString().slice(0, 10),
  VERSION: '1.0.0'
}

skip/warn/throw UnresolvedVariables

Handling of unresolved variables. These are the possible options:

  • skipUnresolvedVariables (Default: false)
    • Skip command arguments where variables have no value
  • warnUnresolvedVariables (Default: true)
    • Warn on command arguments where variables have no value
  • throwUnresolvedVariables (Default: false)
    • Throw exception on command arguments where variables have no value

variablePattern

RegExp pattern for matching variables.

For example: ${SOME_VARIABLE}

Default

/\$\{(.+)\}/

lineIgnorePattern

RegExp pattern for ignoring lines in files when generating arguments.

For example: # Some comment or // Some comment

Default

/^(\#|\/{2,})/

convertVariables

Convert variables based on format. An example would be to convert Windows environment variables to Linux or vice versa.

For example: %FOO% -> $FOO

Possible values
false
 | [
     true,
     {
       from: RegExp;
       to: string;
     }
   ];
Example
[true, { from: /\%([A-Z]+)\%/, to: '$$$1' }]

This would convert all Windows variables %FOO% to Linux variables $FOO

shell

Shell to use for executing commands

Default

/bin/bash

transformers

Transformer is an object which consists of two parts: predicate and replacer.

  • predicate is a function that returns a boolean.
  • replacer is a function that returns a new value.
Example

Here's a simple transformer that surrounds sentences in quotes.

sentencesInQuotes: {
  predicate: (val: string): boolean => {
    if (!val) return false;
    return val.split(' ').length > 1;
  },
  replacer: (val: string): string => `"${val}"`
}

API

IBuilder

options: IBuilderOptions;
/**
 * Create a new command and register it in the builder
 * @param command Main command, e.g. 'docker build'
 * @param filePatterns Command argument prefix and glob file patterns
 * @returns A new command object
 */
createCommand(
  command: string,
  filePatterns?: IArgumentFilePatterns[]
): ICommand;

/**
 * Get all commands that are registered in the builder
 * @returns Array of command objects
 */
getAllCommands(): ICommand[];

ICommand

/**
 * Executes a command
 * @param stdout Callback on STDOUT
 * @param stderr Callback on STDERR
 */
exec(
  stdout?: (chunk: string) => string,
  stderr?: (chunk: string) => string
): ICommandProcess;

/**
 * Prepend an argument
 * @param argument Argument
 */
prependArgument(argument: TCommandArgumentInput): ICommand;

/**
 * Append an argument
 * @param argument Argument
 */
appendArgument(argument: TCommandArgumentInput): ICommand;

/**
 * Get the command string
 */
toString(): string;

/**
 * Get the command string as an array
 */
toArray(): ReadonlyArray<string>;

Roadmap

  • Set up automated TypeScript documentation for APIs
  • Follow Conventional Commits
  • Set up CI to run automated tests
  • Plugin / Middleware architecture
    • Ability to hook into events
    • Ability to add custom behavior with middlewares
    • Ability to create custom transformers
  • CLI
  • Optimizations + asynchronous operations
  • Batch command handling
  • Interactive process attaching, e.g. for docker run -it

Contributing

All contributions are very well appreciated. Just fork this repo and submit a PR!

Just remember to run the following beforehand:

  • yarn test
  • yarn typecheck
  • yarn format
  • yarn lint