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

@tedslittlerobot/commandeer

v1.4.0

Published

A wrapper around CommanderJS with a few common utilities.

Downloads

128

Readme

Commandeer

A wrapper around CommanderJS with a few common utilities.

Also supplies integrations with:

  • CommanderJS A CLI library for NodeJS
  • Listr2 A library for managing concurrent and consecutive task lists in NodeJS
  • Margaret Lanterman A library for managing streaming things to log files
  • Gloucester A library for managing verbosity state

Installation

npm i @tedslittlerobot/commandeer

Usage

TSConfig

There is a base tsconfig.json which can be used as follows. Note that you must still provide all paths, as paths within an extended config are relative to where the config is extended from.

{
  "extends": "./node_modules/@tedslittlerobot/commandeer/tsconfig.json",
  "compilerOptions": {
    "rootDir": "./src",
    "baseUrl": "./",
    "outDir": "build",
    "paths": {
      "src/*": ["./src/*"]
    },
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": ["node_modules"]
}

Writing CLI Apps

Commandeer introduces a convention for registering commands and groups in commander.

src
├── index.ts # The root index.ts should define the program
├── commands
│   ├── config.ts # This should export the Command definition for a command
│   ├── users # This is a sub-command / command group
│   │   ├── index.ts # This should export the Group definition
│   │   ├── details.ts # This should export the Command definition for a command
│   │   └── create.ts # This should export the Command definition for a command
│   └── index.ts # This should export an array of all commands
├── helpers # A directory for generic helpers
│   ├── my-api.ts
│   └── wordart.ts
└── tasks # A directory of Listr2 Task Lists
    └── user-details
		    ├── index.ts # This should export the task list
        ├── api-call.ts # More complex individual tasks may have their own file
        ├── evaluate-study.test.ts # And their own tests
        ├── evaluate-study.test.ts # And their own tests
        ├── io.ts # ... more complex task file
        └── types.ts # And potentially some specific type definitions

Root index.ts

Firstly, your main/root index.ts / entry point should be:

#! /usr/bin/env node
import {env} from 'node:process';
import commands from 'src/commands/index.js';
import {runProgram} from '@tedslittlerobot/commandeer/lib/commander';

runProgram(
	'my-command-name',
	env.COMMANDER_VERSION ?? 'vx.x.x',
	'A vulnerability scan checker for trialmotif',
	commands,
);

Commands Index File commands/index.ts

The commands index file should export an array of all groups and top level commands.

import users from './users/index.js';

const commands = [users];
export default commands;

Command Group Definition commands/users/index.ts

A Command Group contains some configuration options and some sub commands:

import type {CommandGroup} from '@tedslittlerobot/commandeer';
import create from './create.js';
import details from './details.js';

const group: CommandGroup = {
	type: 'group',
	name: 'users',
	description: 'Some commands to peform user actions',
	commands: [create, details],
};

export default group;

Command Definition commands/users/index.ts

A Command Group contains some configuration options and some sub commands:

import type {CommandRegistrar} from '@tedslittlerobot/commandeer/lib/commander';
import {type Command} from 'commander';

type Options = {
	output?: string;
};

const command: CommandRegistrar = {
	type: 'command',
	name: 'details',
	description: 'Get some user details',
	config(command) {
		command
      .argument('<userId>', 'The user ID')
			.option('-o, --output <file>', 'Optional location to store manifest file');
	},
	async action(
    userId: string,
		options: Options,
		command: Command,
	) {
		console.info(`User details for ${userId}`);
	},
	completions() {
		return [];
	},
};

export default command;

Command Definition With Listr2 commands/users/index.ts

A Command Group contains some configuration options and some sub commands:

import type {CommandRegistrar} from '@tedslittlerobot/commandeer/lib/commander';
import {runTasks} from '@tedslittlerobot/commandeer/lib/listr';
import {type Command} from 'commander';
import tasks, {summarise} from 'src/tasks/user-details.js';

type Options = {
	output?: string;
};

const command: CommandRegistrar = {
	type: 'command',
	name: 'details',
	description: 'Get some user details',
	config(command) {
		command
      .argument('<userId>', 'The user ID')
			.option('-o, --output <file>', 'Optional location to store output file');
	},
	async action(
    userId: string,
		options: Options,
		command: Command,
	) {
		summarise( // A summarise method to handle final output
			await runTasks(tasks(options.output)), // And an async run of the relevant tasks
		);
	},
	completions() {
		return [];
	},
};

export default command;

Task List File tasks/user-details/index.ts

import {stderr, stdout} from 'node:process';
import {type ListrTask} from 'listr2';
import chalk, {type ChalkInstance} from 'chalk';
import apiCall from './user-details.api.js';

type MvmTaskContext = {
  payload: Record<string, string>;
	name: string;
};

export default function mvmTasks(
  userId: string,
  outputFile?: string,
  format: ChalkInstance = chalk.cyan.bold,
): Array<ListrTask<MvmTaskContext>> {
  return [
		{
			title: format('Getting user details from API'),
			async task(context, task) {
				context.payload = await apiCall(
          userId,
          ['config option 1', 'config option 1']
				);

        task.title = 'Details retrieved from API';
			},
		},
    {
			title: format('Parsing API Payload'),
			async task(context) {
				context.name = context.payload.name;
			},
		},
  ];
}

export function summarise(({payload, name}): MvmTaskContext) {
  const table = new CliTable3();
  table.push(payload);
  strerr.write(table.toString()); // For user friendly output to stderr

  stdout.write(JSON.stringify({name})); // eg. for piping to stdout
}