Skip to content

Instantly share code, notes, and snippets.

@ethan605
Last active December 11, 2023 19:47
Show Gist options
  • Save ethan605/4c657832d46d12408072fc2acdebf019 to your computer and use it in GitHub Desktop.
Save ethan605/4c657832d46d12408072fc2acdebf019 to your computer and use it in GitHub Desktop.
Boilerplate to quickly write a CLI tool with Node.js
#!/usr/bin/env node
/**
* A script to do something handy in an easy way
*
* For more info, run:
* $ <cli-name> --help
*/
'use strict'
const fs = require('fs')
const COLORS = {
RESET: '\x1b[0m',
BRIGHT: '\x1b[1m',
DIM: '\x1b[2m',
UNDERSCORE: '\x1b[4m',
BLINK: '\x1b[5m',
REVERSE: '\x1b[7m',
HIDDEN: '\x1b[8m',
FG: {
BLACK: '\x1b[30m',
BLUE: '\x1b[34m',
CRIMSON: '\x1b[38m',
CYAN: '\x1b[36m',
GREEN: '\x1b[32m',
MAGENTA: '\x1b[35m',
RED: '\x1b[31m',
WHITE: '\x1b[37m',
YELLOW: '\x1b[33m',
},
BG: {
BLACK: '\x1b[40m',
BLUE: '\x1b[44m',
CRIMSON: '\x1b[48m',
CYAN: '\x1b[46m',
GREEN: '\x1b[42m',
MAGENTA: '\x1b[45m',
RED: '\x1b[41m',
WHITE: '\x1b[47m',
YELLOW: '\x1b[43m',
},
}
const COMMAND = '<cli-name>'
const VERSION = 'v1.0.0'
const PARSED_ARGS = {}
/* Helper functions */
function buildColorMessage(message, ...colors) {
return [...colors, message, COLORS.RESET].join('')
}
function printError(message) {
console.error(buildColorMessage(`Error: ${message}`, COLORS.FG.RED))
}
function verboseLogging(...args) {
if (!PARSED_ARGS.verbose) {
return
}
console.log(...args)
}
function printVersion() {
console.info(`<cli-name> ${VERSION} (c) 2020`)
process.exit(0)
}
function printHelp(errorMessage) {
if (errorMessage) {
printError(`${errorMessage}\n`)
}
console.info(`${COMMAND} - do something handy in an easy way
Usage:
${COMMAND} [options] [flags]
Examples:
${COMMAND} options... flags...
Available options:
-i, --in-file *required* Specify path to input file.
-o, --out-file Specify path to output JSON file.
If not specified, the result will be emitted to STDOUT.
Available flags:
-v, --verbose Verbose logging.
-V, --version Show the current version of the script.
-h, --help Show this message.`)
process.exit(errorMessage ? 1 : 0)
}
function parseOptionValue(name, trigger, params) {
const arg = params.shift()
if (!arg) {
printHelp(`Missing value for ${trigger} option`)
}
return { [name]: arg }
}
function validateOptions(parsedOptions) {
const { inFile } = parsedOptions
if (!inFile) {
printHelp('Missing --in-file|-i param')
}
if (!fs.existsSync(inFile)) {
printError('Input file not found')
process.exit(1)
}
// Other validations
}
function parseArgs() {
const params = process.argv.slice(2)
while (params.length) {
const args0 = params.shift()
switch (args0) {
case '--in-file':
case '-i':
Object.assign(PARSED_ARGS, parseOptionValue('inFile', args0, params))
break
case '--out-file':
case '-o':
Object.assign(PARSED_ARGS, parseOptionValue('outFile', args0, params))
break
case '--verbose':
case '-v':
Object.assign(PARSED_ARGS, { verbose: true })
break
case '--version':
case '-V':
printVersion()
break
case '--help':
case '-h':
printHelp()
break
}
}
validateOptions(PARSED_ARGS)
}
/* Main functions */
function main() {
parseArgs()
const { inFile, outFile } = PARSED_ARGS
const inputJson = JSON.parse(fs.readFileSync(inFile))
const result = JSON.stringify(inputJson, null, 2)
verboseLogging('Reading data from', buildColorMessage(inFile, COLORS.FG.BLUE))
if (!outFile) {
verboseLogging('\nProcessed data:')
console.info(result)
return
}
fs.writeFileSync(outFile, result)
console.info(
`Process written to ${buildColorMessage(outFile, COLORS.FG.GREEN)}`
)
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment