Skip to main content
Deno 2 is finally here 🎉️
Learn more

FAPP

Flags And Positional Parameters

fapp is a convention of how pass command line arguments to functions.

The pattern is:

  • A plein Array
    • with flags first
    • then positional parameters

Where flags is an Object and patameters an Array:

[ flags: Object, ...positionalArguments: string[] ]

This can be easily collected into an Array to pass it to another sub function:

( ...fapp )=> another( ...fapp )

Or destructured/reorganised to use it right now:

( { flag1, flag2 }, firstWordCommand, ...rest )=>

    firstWordCommand == 'sub' && flag1
        ? subCommand( { flag2 }, ...rest )

    :firstWordCommand == 'foo' && flag2
        ? fooCommand( { flag1, bar: 'baz' }, rest[0] )
    
    :help()

Why?

  • Javascript rest parameter ( aka ...varname ) must be last argument, so a command that can act on 1 or more targets are likely to use a rest parameter.
  • Options object to be destructured is a usal pattern.

Usages

Deno

Imports the named export { fapp } that is the parsing function:

import { fapp } from 'x/fapp/mod.ts'

if( itShouldExecuteCommands )
    myCommand( ...fapp(ARGV) )

Or imports the default’s value that is the Deno.args already parsed:

import fapp from 'x/fapp/mod.ts'
const [ flags, ...params ] = fapp

flags.foo ? stuff(params[0]) : params.map( other )

Note you can rename the fapp array as you like

import I_LIKE_CONST_CASE_NAMES from 'x/fapp/mod.ts'

A more complete exemple:

import fapp from 'x/fapp/mod.ts'

export const main = ( { help, format }, command, ...operands )=>
    
    help || command == 'help'
        ? usage( { help, format }, command, ...operands )
    
    :command == 'sub' && operands.length > 1 && format
        ? subtract( ...operands ).formatTo( format )
    
    :command == 'add' && operands.length > 1 && format
        ? add( ...operands ).formatTo( format )
    
    :usage()

import.meta.main
    && main( ...fapp )

Here, fapp module is always loaded, and Deno.args always parsed.

If you want to avoid that to mix a standart libray module that only exports things to be imported and an executable file, you can dynamcaly import only if this scrit is the main module:

export const myAwesomeCommand = ()=> 42

export const help = ()=> '...'

import.meta.main && await import('x/fapp/mod.ts').then(
    ({ default: [ flags, ...params ] })=>

        flags.help ? help()

        :params.map( path=> myAwesomeCommand( flags, path ) )

)

Here because of import.meta.main && that act as a if, nothing is loaded/parsed when this file is imported by another module…