Stone.jsDocs
Paradigm

Extending

Create CLI commands

A command is a handler for a command-line cause. With the Node CLI adapter enabled, you declare commands with @Command (or defineCommand), read parsed arguments and flags off the event, and return output. The domain and its services are the same ones your HTTP routes use.

#Enable the adapter

Commands need @stone-js/node-cli-adapter. Add @NodeConsole() (or the blueprint) to the manifest; the adapter page covers setup. This page is the command author's deep reference.

#Declaring a command

app/commands/Import.tsdeclarativeimperative
import { Command } from '@stone-js/node-cli-adapter'

@Command({
  name: 'tasks:import',
  alias: 'import',
  args: ['<file>', '[--dry-run]'],
  description: 'Import tasks from a CSV file.'
})
export class ImportCommand {
  constructor ({ tasks, logger }: { tasks: TaskService, logger: ILogger }) {
    this.tasks = tasks; this.logger = logger
  }

  async handle (event: NodeCliEvent) {
    const file = event.get<string>('file')            // positional <file>
    const dryRun = event.get<boolean>('dry-run', false)  // flag
    const rows = await readCsv(file)
    if (dryRun) return `Would import ${rows.length} tasks`
    rows.forEach((r) => this.tasks.add(r.title))
    this.logger.info('imported', { count: rows.length })
    return `Imported ${rows.length} tasks`
  }
}
import { defineCommand } from '@stone-js/node-cli-adapter'

const ImportCommand = ({ tasks }) => ({
  handle: async (event) => {
    const rows = await readCsv(event.get('file'))
    rows.forEach((r) => tasks.add(r.title))
    return `Imported ${rows.length} tasks`
  }
})

export const commands = [
  defineCommand(ImportCommand, {
    name: 'tasks:import',
    args: ['<file>', '[--dry-run]'],
    description: 'Import tasks from a CSV file.'
  }, true)
]

#Arguments and flags

Declare inputs in args; the adapter parses them and puts them on the event, read with event.get() like any intention. The conventions:

SyntaxTypeDescription
<file>required positionalA required argument; the command errors without it.
[file]optional positionalAn optional argument.
[--dry-run]boolean flagA flag, read as a boolean.
[--limit=<n>]valued flagA flag that carries a value.

#Command options

OptionTypeDescription
name*stringThe command name, e.g. "tasks:import".
aliasstring | string[]Alternative name(s).
argsstring | string[]Declared arguments and flags.
descriptionstringShown in help output.

#Output

Return a string (or structured data) and the adapter writes it out; throw a domain error and it is reported with a non-zero exit, mapped the same way HTTP errors are. Use the injected logger for progress, the return value for the result.

terminalbash
node app tasks:import ./seed.csv --dry-run
node app import ./seed.csv          # via the alias

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)