@adonisjs/ace

repository·14.x·Indexed 19 days ago

https://github.com/adonisjs/ace

A lightweight, test-oriented CLI framework for Node.js designed for building command-line applications. It provides a clean API for creating custom commands by extending BaseCommand, managing execution via a Kernel, and parsing input with a dedicated Parser. The framework includes built-in support for defining arguments and flags, a dedicated ExceptionHandler for error management, and UI helpers for interactive interfaces.

Tokens
10K
Snippets
44
Records
54
Agent score
64%

What's inside @adonisjs/ace

  1. Introduction to @adonisjs/ace

    14.x
    Ace is a lightweight command-line framework for Node.js designed specifically for creating CLI commands. It features a clean API and is built with a strong emphasis on testability.
  2. Access command arguments and flags as properties

    14.x

    After a command is executed (via exec()), the hydrate() method is called internally. This maps the parsed CLI arguments and flags to properties on the command instance using the names provided during definition. This allows you to access them directly via this.propertyName inside your run() method.

    // If you defined: Command.defineArgument('entity', { type: 'string' })
    // And ran: ace my:command user
    
    async run() {
      console.log(this.entity) // 'user'
    }
  3. Understand the ParsedOutput structure

    14.x

    When a command is executed, the CLI arguments are parsed into a ParsedOutput object. This object is passed to your command's validate method and is used to populate the command instance.

    Key fields in ParsedOutput:

    • nodeArgs: The original arguments passed to the Node.js process.
    • args: An array of the parsed positional arguments.
    • _: Array<string | number>: Leftover arguments after parsing flags and args.
    • unknownFlags: An array of flags that were not recognized by the parser.
    • flags: A dictionary of parsed flags, where keys are flag names and values are the parsed values.
    export type ParsedOutput = YargsOutput & {
      nodeArgs: string[]
      args: (any | any[])[]
      _: Array<string | number>
      unknownFlags: string[]
      flags: {
        [argName: string]: any
      }
    }
  4. Implement self-handling errors

    14.x
    The ExceptionHandler checks if an error object has its own render method. If an error implements a render(error, kernel) function, the ExceptionHandler will delegate the rendering responsibility to that error object instead of using its internal logic.
  5. Create a custom CLI command with BaseCommand

    14.x

    To create a new Ace command, extend the BaseCommand class and implement the run() method. You define the command's identity using static properties like commandName and description. Arguments and flags are registered using static methods, which allows them to be accessed as properties on the command instance after hydration.

    export class MyCommand extends BaseCommand {
      static commandName = 'my:command'
      static description = 'My custom command'
    
      async run() {
        this.logger.info('Hello from my command!')
      }
    }
  6. Configure ESLint using @adonisjs/eslint-config

    14.x

    To use the recommended ESLint configuration for AdonisJS projects, import configPkg from @adonisjs/eslint-config and export the result of calling configPkg() in your eslint.config.js file. This provides a pre-configured ESLint setup tailored for the AdonisJS ecosystem.

    import { configPkg } from '@adonisjs/eslint-config'
    export default configPkg()
  7. Register command loaders in the Kernel

    14.x

    The Kernel uses loaders to discover and register commands. You must call addLoader before calling boot or handle. You can provide a loader instance or a function that returns a promise of a loader (for lazy loading).

    If multiple loaders return the same command, the one from the most recent loader takes precedence.

    // Using a direct instance
    kernel.addLoader(new FsLoader('./commands'))
    
    // Using a lazy-loading function
    kernel.addLoader(() => import('./lazy-loader').then(m => new m.LazyLoader()))
  8. Initialize and use the Ace Kernel

    14.x

    The Kernel is the main entry point for an Ace console application. It manages command registration, loading, and execution. You can create a new instance using Kernel.create(), which sets up a default executor and a default command (usually ListCommand).

    To run a CLI application using the kernel, pass the process arguments to kernel.handle(argv).

    const kernel = Kernel.create()
    
    // Configure the kernel
    kernel.defineFlag('help', {
      type: 'boolean',
      alias: 'h',
      description: 'Display help'
    })
    
    kernel.addLoader(new FsLoader('./commands'))
    kernel.info.set('App version', '1.1.1')
    
    // Execute the command line arguments
    await kernel.handle(process.argv.slice(2))
  9. Configure debug mode in ExceptionHandler

    14.x

    The ExceptionHandler has a debug property (boolean, defaults to true).

    • When debug is true: Uncaught errors are pretty-printed to the console using Youch for detailed stack traces.
    • When debug is false: The error is logged as a fatal error via the kernel's logger, providing a more concise output suitable for production environments.
    const handler = new ExceptionHandler()
    handler.debug = false
  10. Configure command options

    14.x

    The CommandOptions object allows you to control the behavior of the command during execution.

    • allowUnknownFlags: If false (default), the command will fail to run if the user provides any flags that are not explicitly defined. Set to true to permit extra flags.
    • staysAlive: If true, the kernel will not trigger the termination process unless the command explicitly calls the terminate method. This is useful for long-running processes or watchers.
    export type CommandOptions = {
      allowUnknownFlags?: boolean
      staysAlive?: boolean
    } & Record<string, any>
  11. Use ArgumentFormatter to format CLI argument displays

    14.x

    The ArgumentFormatter class is used to format command arguments for terminal output, following the docopt.org specification. It allows you to generate consistent visual representations for argument names, descriptions, and usage patterns (like required vs. optional or spread arguments) using provided color utilities.

    To use it, instantiate the class with an Argument configuration object and a colors object (of type UIPrimitives['colors']).

    const formatter = new ArgumentFormatter(argument, colors)
    const formatted = formatter.formatOption() // e.g., '<entity>'
    const listOption = formatter.formatListOption() // e.g., '  entity  '
  12. Customize exception rendering with ExceptionHandler

    14.x

    The ExceptionHandler class is the default handler for Ace exceptions. You can extend this class to override the render method and implement custom error handling logic for your CLI application.

    When implementing a custom handler, you can call await super.render(error, kernel) to maintain the default behavior (such as handling command-not-found errors or using Youch for pretty-printing in debug mode) while adding your own logic.

    import { ExceptionHandler } from '@adonisjs/ace'
    
    export class MyExceptionHandler extends ExceptionHandler {
      async render(error: unknown, kernel: Kernel<any>) {
        // Custom error handling logic here
        
        // Fallback to default Ace error rendering
        await super.render(error, kernel)
      }
    }