├── .github └── workflows │ └── node.js.yml ├── LICENSE ├── README.hbs ├── README.md ├── dist └── index.cjs ├── example ├── advanced │ ├── arg-data.js │ └── git.js ├── minimist.js ├── screens │ └── command-list.png ├── simple.js ├── synopsis.js └── usage.js ├── index.js ├── option.js ├── package-lock.json ├── package.json └── test.js /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 2 | 3 | name: Node.js CI 4 | 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest] 19 | node-version: [12, 14, 16, 18, 20, 22] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: 'npm' 28 | - run: npm install 29 | - run: npm i -g @75lb/nature 30 | - run: npm run test:ci 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-25 Lloyd Brookes <75pound@gmail.com> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.hbs: -------------------------------------------------------------------------------- 1 | [![view on npm](https://badgen.net/npm/v/command-line-commands)](https://www.npmjs.org/package/command-line-commands) 2 | [![npm module downloads](https://badgen.net/npm/dt/command-line-commands)](https://www.npmjs.org/package/command-line-commands) 3 | [![Gihub repo dependents](https://badgen.net/github/dependents-repo/75lb/command-line-commands)](https://github.com/75lb/command-line-commands/network/dependents?dependent_type=REPOSITORY) 4 | [![Gihub package dependents](https://badgen.net/github/dependents-pkg/75lb/command-line-commands)](https://github.com/75lb/command-line-commands/network/dependents?dependent_type=PACKAGE) 5 | [![Node.js CI](https://github.com/75lb/command-line-commands/actions/workflows/node.js.yml/badge.svg)](https://github.com/75lb/command-line-commands/actions/workflows/node.js.yml) 6 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](https://github.com/feross/standard) 7 | 8 | # command-line-commands 9 | 10 | A lightweight module to help build a git-like command interface for your app. 11 | 12 | Its job is to extract the command (the first argument, unless it's an option), check it's valid and either return it or throw. From there, you can parse the remaining args using your preferred option parser (e.g. [command-line-args](https://github.com/75lb/command-line-args), [minimist](https://github.com/substack/minimist) etc.). 13 | 14 | ## Synopsis 15 | 16 | Create a list of valid commands (`null` represents "no command"). Supply it to `commandLineCommands()`, receiving back an object with two properties: `command` (the supplied command) and `argv` (the remainder of the command line args): 17 | ```js 18 | const commandLineCommands = require('command-line-commands') 19 | 20 | const validCommands = [ null, 'clean', 'update', 'install' ] 21 | const { command, argv } = commandLineCommands(validCommands) 22 | 23 | /* print the command and remaining command-line args */ 24 | console.log('command: %s', command) 25 | console.log('argv: %s', JSON.stringify(argv)) 26 | ``` 27 | 28 | We'll assume the above script is installed as `example`. Since the `validCommands` list includes `null`, running it without a command is valid: 29 | ``` 30 | $ example 31 | command: null 32 | argv: [] 33 | ``` 34 | 35 | Running `example` with no command and one option: 36 | ``` 37 | $ example --verbose 38 | command: null 39 | argv: ["--verbose"] 40 | ``` 41 | 42 | Running `example` with both a command and an option: 43 | ``` 44 | $ example install --save something 45 | command: install 46 | argv: ["--save","something"] 47 | ``` 48 | 49 | Running `example` without a valid command will cause `commandLineCommands()` to throw. 50 | 51 | From here, you can make a decision how to proceed based on the `command` and `argv` received. For example, if no command (`null`) was passed, you could parse the remaining `argv` for general options (in this case using [command-line-args](https://github.com/75lb/command-line-args)): 52 | 53 | ```js 54 | if (command === null) { 55 | const commandLineArgs = require('command-line-args') 56 | const optionDefinitions = [ 57 | { name: 'version', type: Boolean } 58 | ] 59 | 60 | // pass in the `argv` returned by `commandLineCommands()` 61 | const options = commandLineArgs(optionDefinitions, { argv }) 62 | 63 | if (options.version) { 64 | console.log('version 1.0.1') 65 | } 66 | } 67 | ``` 68 | 69 | The same example, using [minimist](https://github.com/substack/minimist): 70 | 71 | ```js 72 | if (command === null) { 73 | const minimist = require('minimist') 74 | 75 | // pass in the `argv` returned by `commandLineCommands()`` 76 | const options = minimist(argv) 77 | 78 | if (options.version) { 79 | console.log('version 1.0.1') 80 | } 81 | } 82 | ``` 83 | 84 | ## More examples 85 | 86 | Both examples use [command-line-args](https://github.com/75lb/command-line-args) for option-parsing. 87 | 88 | - [Simple](https://github.com/75lb/command-line-commands/blob/master/example/simple.js): A basic app with a couple of commands. 89 | - [Advanced](https://github.com/75lb/command-line-commands/blob/master/example/advanced/git.js): A more complete example, implementing part of the git command interface. 90 | 91 | ## Usage guides 92 | 93 | Usage guides can be generated by [command-line-usage](https://github.com/75lb/command-line-usage). Here is a simple example ([code](https://github.com/75lb/command-line-commands/blob/master/example/usage.js)): 94 | 95 | ![usage](https://raw.githubusercontent.com/75lb/command-line-commands/master/example/screens/command-list.png) 96 | 97 | # API Reference 98 | {{#module name="command-line-commands"}} 99 | {{>body~}} 100 | {{>member-index~}} 101 | {{>separator~}} 102 | {{>members~}} 103 | {{/module}} 104 | 105 | * * * 106 | 107 | © 2015-25 Lloyd Brookes \<75pound@gmail.com\>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![view on npm](https://badgen.net/npm/v/command-line-commands)](https://www.npmjs.org/package/command-line-commands) 2 | [![npm module downloads](https://badgen.net/npm/dt/command-line-commands)](https://www.npmjs.org/package/command-line-commands) 3 | [![Gihub repo dependents](https://badgen.net/github/dependents-repo/75lb/command-line-commands)](https://github.com/75lb/command-line-commands/network/dependents?dependent_type=REPOSITORY) 4 | [![Gihub package dependents](https://badgen.net/github/dependents-pkg/75lb/command-line-commands)](https://github.com/75lb/command-line-commands/network/dependents?dependent_type=PACKAGE) 5 | [![Node.js CI](https://github.com/75lb/command-line-commands/actions/workflows/node.js.yml/badge.svg)](https://github.com/75lb/command-line-commands/actions/workflows/node.js.yml) 6 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](https://github.com/feross/standard) 7 | 8 | # command-line-commands 9 | 10 | A lightweight module to help build a git-like command interface for your app. 11 | 12 | Its job is to extract the command (the first argument, unless it's an option), check it's valid and either return it or throw. From there, you can parse the remaining args using your preferred option parser (e.g. [command-line-args](https://github.com/75lb/command-line-args), [minimist](https://github.com/substack/minimist) etc.). 13 | 14 | ## Synopsis 15 | 16 | Create a list of valid commands (`null` represents "no command"). Supply it to `commandLineCommands()`, receiving back an object with two properties: `command` (the supplied command) and `argv` (the remainder of the command line args): 17 | ```js 18 | const commandLineCommands = require('command-line-commands') 19 | 20 | const validCommands = [ null, 'clean', 'update', 'install' ] 21 | const { command, argv } = commandLineCommands(validCommands) 22 | 23 | /* print the command and remaining command-line args */ 24 | console.log('command: %s', command) 25 | console.log('argv: %s', JSON.stringify(argv)) 26 | ``` 27 | 28 | We'll assume the above script is installed as `example`. Since the `validCommands` list includes `null`, running it without a command is valid: 29 | ``` 30 | $ example 31 | command: null 32 | argv: [] 33 | ``` 34 | 35 | Running `example` with no command and one option: 36 | ``` 37 | $ example --verbose 38 | command: null 39 | argv: ["--verbose"] 40 | ``` 41 | 42 | Running `example` with both a command and an option: 43 | ``` 44 | $ example install --save something 45 | command: install 46 | argv: ["--save","something"] 47 | ``` 48 | 49 | Running `example` without a valid command will cause `commandLineCommands()` to throw. 50 | 51 | From here, you can make a decision how to proceed based on the `command` and `argv` received. For example, if no command (`null`) was passed, you could parse the remaining `argv` for general options (in this case using [command-line-args](https://github.com/75lb/command-line-args)): 52 | 53 | ```js 54 | if (command === null) { 55 | const commandLineArgs = require('command-line-args') 56 | const optionDefinitions = [ 57 | { name: 'version', type: Boolean } 58 | ] 59 | 60 | // pass in the `argv` returned by `commandLineCommands()` 61 | const options = commandLineArgs(optionDefinitions, { argv }) 62 | 63 | if (options.version) { 64 | console.log('version 1.0.1') 65 | } 66 | } 67 | ``` 68 | 69 | The same example, using [minimist](https://github.com/substack/minimist): 70 | 71 | ```js 72 | if (command === null) { 73 | const minimist = require('minimist') 74 | 75 | // pass in the `argv` returned by `commandLineCommands()`` 76 | const options = minimist(argv) 77 | 78 | if (options.version) { 79 | console.log('version 1.0.1') 80 | } 81 | } 82 | ``` 83 | 84 | ## More examples 85 | 86 | Both examples use [command-line-args](https://github.com/75lb/command-line-args) for option-parsing. 87 | 88 | - [Simple](https://github.com/75lb/command-line-commands/blob/master/example/simple.js): A basic app with a couple of commands. 89 | - [Advanced](https://github.com/75lb/command-line-commands/blob/master/example/advanced/git.js): A more complete example, implementing part of the git command interface. 90 | 91 | ## Usage guides 92 | 93 | Usage guides can be generated by [command-line-usage](https://github.com/75lb/command-line-usage). Here is a simple example ([code](https://github.com/75lb/command-line-commands/blob/master/example/usage.js)): 94 | 95 | ![usage](https://raw.githubusercontent.com/75lb/command-line-commands/master/example/screens/command-list.png) 96 | 97 | # API Reference 98 | **Example** 99 | ```js 100 | const commandLineCommands = require('command-line-commands') 101 | ``` 102 | 103 | 104 | ### commandLineCommands(commands, [argv]) ⇒ Object ⏏ 105 | Parses the `argv` value supplied (or `process.argv` by default), extracting and returning the `command` and remainder of `argv`. The command will be the first value in the `argv` array unless it is an option (e.g. `--help`). 106 | 107 | **Kind**: Exported function 108 | **Throws**: 109 | 110 | - `INVALID_COMMAND` - user supplied a command not specified in `commands`. 111 | 112 | 113 | | Param | Type | Description | 114 | | --- | --- | --- | 115 | | commands | string \| Array.<string> | One or more command strings, one of which the user must supply. Include `null` to represent "no command" (effectively making a command optional). | 116 | | [argv] | Array.<string> | An argv array, defaults to the global `process.argv` if not supplied. | 117 | 118 | 119 | * * * 120 | 121 | © 2015-25 Lloyd Brookes \<75pound@gmail.com\>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). 122 | -------------------------------------------------------------------------------- /dist/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var arrayify = require('array-back'); 4 | 5 | /** 6 | * A module for testing for and extracting names from options (e.g. `--one`, `-o`) 7 | */ 8 | 9 | class Arg { 10 | constructor (re) { 11 | this.re = re; 12 | } 13 | 14 | test (arg) { 15 | return this.re.test(arg) 16 | } 17 | } 18 | 19 | const isShort = new Arg(/^-([^\d-])$/); 20 | const isLong = new Arg(/^--(\S+)/); 21 | const isCombined = new Arg(/^-([^\d-]{2,})$/); 22 | const isOption = function (arg) { 23 | return isShort.test(arg) || isLong.test(arg) || isCombined.test(arg) 24 | }; 25 | 26 | /** 27 | * @module command-line-commands 28 | * @example 29 | * const commandLineCommands = require('command-line-commands') 30 | */ 31 | 32 | /** 33 | * Parses the `argv` value supplied (or `process.argv` by default), extracting and returning the `command` and remainder of `argv`. The command will be the first value in the `argv` array unless it is an option (e.g. `--help`). 34 | * 35 | * @param {string|string[]} - One or more command strings, one of which the user must supply. Include `null` to represent "no command" (effectively making a command optional). 36 | * @param [argv] {string[]} - An argv array, defaults to the global `process.argv` if not supplied. 37 | * @returns {{ command: string, argv: string[] }} 38 | * @throws `INVALID_COMMAND` - user supplied a command not specified in `commands`. 39 | * @alias module:command-line-commands 40 | */ 41 | function commandLineCommands (commands, argv) { 42 | if (!commands || (Array.isArray(commands) && !commands.length)) { 43 | throw new Error('Please supply one or more commands') 44 | } 45 | if (argv) { 46 | argv = arrayify(argv); 47 | } else { 48 | /* if no argv supplied, assume we are parsing process.argv. */ 49 | /* never modify the global process.argv directly. */ 50 | argv = process.argv.slice(0); 51 | argv.splice(0, 2); 52 | } 53 | 54 | /* the command is the first arg, unless it's an option (e.g. --help) */ 55 | const command = (isOption(argv[0]) || !argv.length) ? null : argv.shift(); 56 | 57 | if (arrayify(commands).indexOf(command) === -1) { 58 | const err = new Error('Command not recognised: ' + command); 59 | err.command = command; 60 | err.name = 'INVALID_COMMAND'; 61 | throw err 62 | } 63 | 64 | return { command, argv } 65 | } 66 | 67 | module.exports = commandLineCommands; 68 | -------------------------------------------------------------------------------- /example/advanced/arg-data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | null: { 3 | definitions: [ 4 | { name: 'version', alias: 'v', type: Boolean, description: 'Print the version number.' } 5 | ], 6 | usage: [ 7 | { 8 | header: 'git', 9 | content: 'Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.' 10 | }, 11 | { 12 | header: 'synopsis', 13 | content: '$ git ' 14 | }, 15 | { 16 | header: 'Command List', 17 | content: [ 18 | { name: 'help', summary: 'Display help information about Git.' }, 19 | { name: 'commit', summary: 'Record changes to the repository.' } 20 | ] 21 | } 22 | ] 23 | }, 24 | help: { 25 | definitions: [ 26 | { name: 'topic', type: String, description: 'the topic to display help on', defaultOption: true } 27 | ], 28 | usage: [ 29 | { 30 | header: 'git help', 31 | content: 'Git help about a git command' 32 | }, 33 | { 34 | header: 'synopsis', 35 | content: '$ git help ' 36 | } 37 | ] 38 | }, 39 | commit: { 40 | definitions: [ 41 | { name: 'message', alias: 'm', type: String, description: 'Commit message.' } 42 | ], 43 | usage: [ 44 | { 45 | header: 'git commit', 46 | content: 'Commit some work.' 47 | }, 48 | { 49 | header: 'synopsis', 50 | content: '$ git commit [--message] ' 51 | } 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /example/advanced/git.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | const commandLineCommands = require('../../') 4 | const commandLineArgs = require('command-line-args') 5 | const commandLineUsage = require('command-line-usage') 6 | const argData = require('./arg-data') 7 | 8 | const { command, argv } = commandLineCommands([ null, 'help', 'commit' ]) 9 | 10 | /* important: pass in the argv returned by `commandLineCommands()` */ 11 | const options = commandLineArgs(argData[command].definitions, { argv }) 12 | const usage = commandLineUsage(argData[command].usage) 13 | 14 | switch (command) { 15 | case null: 16 | if (options.version) { 17 | console.log('version 90') 18 | } else { 19 | console.log(usage) 20 | } 21 | break 22 | 23 | case 'help': 24 | if (options.topic) { 25 | console.log(commandLineUsage(argData[options.topic].usage)) 26 | } else { 27 | console.log(commandLineUsage(argData.help.usage)) 28 | } 29 | break 30 | 31 | case 'commit': 32 | if (options.message) { 33 | console.log('commit: ' + options.message) 34 | } else { 35 | console.log(usage) 36 | } 37 | break 38 | } 39 | -------------------------------------------------------------------------------- /example/minimist.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const commandLineCommands = require('../') 3 | const minimist = require('minimist') 4 | 5 | const validCommands = [ 'load', 'print' ] 6 | const { command, argv } = commandLineCommands(validCommands) 7 | 8 | /* pass in the argv returned by `commandLineCommands()` */ 9 | const options = minimist(argv) 10 | 11 | switch (command) { 12 | case 'load': 13 | if (options.file) { 14 | console.log(`Loading: ${options.file}`) 15 | } else { 16 | console.log('please supply a filename') 17 | } 18 | break 19 | 20 | case 'print': 21 | console.log('Printing %s', options.colour ? 'in colour' : 'in B&W' ) 22 | break 23 | } 24 | -------------------------------------------------------------------------------- /example/screens/command-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75lb/command-line-commands/800b3b9111e7f6da6e1b647a5ef46ce77376fbc6/example/screens/command-list.png -------------------------------------------------------------------------------- /example/simple.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const commandLineCommands = require('../') 3 | const commandLineArgs = require('command-line-args') 4 | 5 | const validCommands = [ 'load', 'print' ] 6 | const { command, argv } = commandLineCommands(validCommands) 7 | 8 | const optionDefinitions = { 9 | load: [ 10 | { name: 'file', type: String } 11 | ], 12 | print: [ 13 | { name: 'colour', type: Boolean } 14 | ] 15 | } 16 | 17 | /* important: pass in the argv returned by `commandLineCommands()` */ 18 | const options = commandLineArgs(optionDefinitions[command], { argv }) 19 | 20 | switch (command) { 21 | case 'load': 22 | if (options.file) { 23 | console.log(`Loading: ${options.file}`) 24 | } else { 25 | console.log('please supply a filename') 26 | } 27 | break 28 | 29 | case 'print': 30 | console.log('Printing %s', options.colour ? 'in colour' : 'in B&W' ) 31 | break 32 | } 33 | -------------------------------------------------------------------------------- /example/synopsis.js: -------------------------------------------------------------------------------- 1 | const commandLineCommands = require('../') 2 | 3 | const validCommands = [ null, 'clean', 'update', 'install' ] 4 | const { command, argv } = commandLineCommands(validCommands) 5 | 6 | console.log('command: %s', command) 7 | console.log('argv: %s', JSON.stringify(argv)) 8 | -------------------------------------------------------------------------------- /example/usage.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const commandLineUsage = require('command-line-usage') 3 | 4 | const sections = [ 5 | { 6 | header: 'Example App', 7 | content: 'Generates something [italic]{very} important.' 8 | }, 9 | { 10 | header: 'Synopsis', 11 | content: '$ app ' 12 | }, 13 | { 14 | header: 'Command List', 15 | content: [ 16 | { name: 'help', summary: 'Display help information about Git.' }, 17 | { name: 'commit', summary: 'Record changes to the repository.' }, 18 | { name: 'Version', summary: 'Print the version.' }, 19 | { name: 'etc', summary: 'Etc.' } 20 | ] 21 | } 22 | ] 23 | 24 | const usage = commandLineUsage(sections) 25 | console.log(usage) 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import arrayify from 'array-back' 2 | import { isOption } from './option.js' 3 | 4 | /** 5 | * @module command-line-commands 6 | * @example 7 | * const commandLineCommands = require('command-line-commands') 8 | */ 9 | 10 | /** 11 | * Parses the `argv` value supplied (or `process.argv` by default), extracting and returning the `command` and remainder of `argv`. The command will be the first value in the `argv` array unless it is an option (e.g. `--help`). 12 | * 13 | * @param {string|string[]} - One or more command strings, one of which the user must supply. Include `null` to represent "no command" (effectively making a command optional). 14 | * @param [argv] {string[]} - An argv array, defaults to the global `process.argv` if not supplied. 15 | * @returns {{ command: string, argv: string[] }} 16 | * @throws `INVALID_COMMAND` - user supplied a command not specified in `commands`. 17 | * @alias module:command-line-commands 18 | */ 19 | function commandLineCommands (commands, argv) { 20 | if (!commands || (Array.isArray(commands) && !commands.length)) { 21 | throw new Error('Please supply one or more commands') 22 | } 23 | if (argv) { 24 | argv = arrayify(argv) 25 | } else { 26 | /* if no argv supplied, assume we are parsing process.argv. */ 27 | /* never modify the global process.argv directly. */ 28 | argv = process.argv.slice(0) 29 | argv.splice(0, 2) 30 | } 31 | 32 | /* the command is the first arg, unless it's an option (e.g. --help) */ 33 | const command = (isOption(argv[0]) || !argv.length) ? null : argv.shift() 34 | 35 | if (arrayify(commands).indexOf(command) === -1) { 36 | const err = new Error('Command not recognised: ' + command) 37 | err.command = command 38 | err.name = 'INVALID_COMMAND' 39 | throw err 40 | } 41 | 42 | return { command, argv } 43 | } 44 | 45 | export default commandLineCommands 46 | -------------------------------------------------------------------------------- /option.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A module for testing for and extracting names from options (e.g. `--one`, `-o`) 3 | */ 4 | 5 | class Arg { 6 | constructor (re) { 7 | this.re = re 8 | } 9 | 10 | test (arg) { 11 | return this.re.test(arg) 12 | } 13 | } 14 | 15 | const isShort = new Arg(/^-([^\d-])$/) 16 | const isLong = new Arg(/^--(\S+)/) 17 | const isCombined = new Arg(/^-([^\d-]{2,})$/) 18 | const isOption = function (arg) { 19 | return isShort.test(arg) || isLong.test(arg) || isCombined.test(arg) 20 | } 21 | const optEquals = new Arg(/^(--\S+)=(.*)/) 22 | 23 | export { isShort, isLong, isCombined, isOption, optEquals } 24 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "command-line-commands", 3 | "version": "4.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "command-line-commands", 9 | "version": "4.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "array-back": "^6.2.2" 13 | }, 14 | "devDependencies": { 15 | "command-line-args": "^6.0.0", 16 | "command-line-usage": "^7.0.3", 17 | "minimist": "^1.2.8" 18 | }, 19 | "engines": { 20 | "node": ">=12.17" 21 | } 22 | }, 23 | "node_modules/ansi-styles": { 24 | "version": "4.3.0", 25 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 26 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 27 | "dev": true, 28 | "license": "MIT", 29 | "dependencies": { 30 | "color-convert": "^2.0.1" 31 | }, 32 | "engines": { 33 | "node": ">=8" 34 | }, 35 | "funding": { 36 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 37 | } 38 | }, 39 | "node_modules/array-back": { 40 | "version": "6.2.2", 41 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", 42 | "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", 43 | "license": "MIT", 44 | "engines": { 45 | "node": ">=12.17" 46 | } 47 | }, 48 | "node_modules/chalk": { 49 | "version": "4.1.2", 50 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 51 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 52 | "dev": true, 53 | "license": "MIT", 54 | "dependencies": { 55 | "ansi-styles": "^4.1.0", 56 | "supports-color": "^7.1.0" 57 | }, 58 | "engines": { 59 | "node": ">=10" 60 | }, 61 | "funding": { 62 | "url": "https://github.com/chalk/chalk?sponsor=1" 63 | } 64 | }, 65 | "node_modules/chalk-template": { 66 | "version": "0.4.0", 67 | "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", 68 | "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", 69 | "dev": true, 70 | "license": "MIT", 71 | "dependencies": { 72 | "chalk": "^4.1.2" 73 | }, 74 | "engines": { 75 | "node": ">=12" 76 | }, 77 | "funding": { 78 | "url": "https://github.com/chalk/chalk-template?sponsor=1" 79 | } 80 | }, 81 | "node_modules/color-convert": { 82 | "version": "2.0.1", 83 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 84 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 85 | "dev": true, 86 | "license": "MIT", 87 | "dependencies": { 88 | "color-name": "~1.1.4" 89 | }, 90 | "engines": { 91 | "node": ">=7.0.0" 92 | } 93 | }, 94 | "node_modules/color-name": { 95 | "version": "1.1.4", 96 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 97 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 98 | "dev": true, 99 | "license": "MIT" 100 | }, 101 | "node_modules/command-line-args": { 102 | "version": "6.0.0", 103 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.0.tgz", 104 | "integrity": "sha512-zDdHxHzlCp/gA1gy0VtPK3YL0Aob3ijJdwZ7H3HSl55hh8EziLtRlyj/od8EGRJfX8IjussC/mQkScl2Ms5Suw==", 105 | "dev": true, 106 | "license": "MIT", 107 | "dependencies": { 108 | "array-back": "^6.2.2", 109 | "find-replace": "^5.0.1", 110 | "lodash.camelcase": "^4.3.0", 111 | "typical": "^7.1.1" 112 | }, 113 | "engines": { 114 | "node": ">=12.20" 115 | } 116 | }, 117 | "node_modules/command-line-usage": { 118 | "version": "7.0.3", 119 | "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", 120 | "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", 121 | "dev": true, 122 | "license": "MIT", 123 | "dependencies": { 124 | "array-back": "^6.2.2", 125 | "chalk-template": "^0.4.0", 126 | "table-layout": "^4.1.0", 127 | "typical": "^7.1.1" 128 | }, 129 | "engines": { 130 | "node": ">=12.20.0" 131 | } 132 | }, 133 | "node_modules/find-replace": { 134 | "version": "5.0.1", 135 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.1.tgz", 136 | "integrity": "sha512-o5/Y8HrCNRuFF5rdNTkX8Vhv6kTFTV0t1zIoigwlCdbkA9qaapRzxvWPND2VvlFa9LBI05Q1i8ml/saMqkOJUQ==", 137 | "dev": true, 138 | "license": "MIT", 139 | "dependencies": { 140 | "array-back": "^6.2.2" 141 | }, 142 | "engines": { 143 | "node": ">=14" 144 | } 145 | }, 146 | "node_modules/has-flag": { 147 | "version": "4.0.0", 148 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 149 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 150 | "dev": true, 151 | "license": "MIT", 152 | "engines": { 153 | "node": ">=8" 154 | } 155 | }, 156 | "node_modules/lodash.camelcase": { 157 | "version": "4.3.0", 158 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 159 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", 160 | "dev": true 161 | }, 162 | "node_modules/minimist": { 163 | "version": "1.2.8", 164 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 165 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 166 | "dev": true, 167 | "license": "MIT", 168 | "funding": { 169 | "url": "https://github.com/sponsors/ljharb" 170 | } 171 | }, 172 | "node_modules/supports-color": { 173 | "version": "7.2.0", 174 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 175 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 176 | "dev": true, 177 | "license": "MIT", 178 | "dependencies": { 179 | "has-flag": "^4.0.0" 180 | }, 181 | "engines": { 182 | "node": ">=8" 183 | } 184 | }, 185 | "node_modules/table-layout": { 186 | "version": "4.1.1", 187 | "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", 188 | "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", 189 | "dev": true, 190 | "license": "MIT", 191 | "dependencies": { 192 | "array-back": "^6.2.2", 193 | "wordwrapjs": "^5.1.0" 194 | }, 195 | "engines": { 196 | "node": ">=12.17" 197 | } 198 | }, 199 | "node_modules/typical": { 200 | "version": "7.1.1", 201 | "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", 202 | "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", 203 | "dev": true, 204 | "license": "MIT", 205 | "engines": { 206 | "node": ">=12.17" 207 | } 208 | }, 209 | "node_modules/wordwrapjs": { 210 | "version": "5.1.0", 211 | "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", 212 | "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", 213 | "dev": true, 214 | "license": "MIT", 215 | "engines": { 216 | "node": ">=12.17" 217 | } 218 | } 219 | }, 220 | "dependencies": { 221 | "ansi-styles": { 222 | "version": "4.3.0", 223 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 224 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 225 | "dev": true, 226 | "requires": { 227 | "color-convert": "^2.0.1" 228 | } 229 | }, 230 | "array-back": { 231 | "version": "6.2.2", 232 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", 233 | "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==" 234 | }, 235 | "chalk": { 236 | "version": "4.1.2", 237 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 238 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 239 | "dev": true, 240 | "requires": { 241 | "ansi-styles": "^4.1.0", 242 | "supports-color": "^7.1.0" 243 | } 244 | }, 245 | "chalk-template": { 246 | "version": "0.4.0", 247 | "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", 248 | "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", 249 | "dev": true, 250 | "requires": { 251 | "chalk": "^4.1.2" 252 | } 253 | }, 254 | "color-convert": { 255 | "version": "2.0.1", 256 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 257 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 258 | "dev": true, 259 | "requires": { 260 | "color-name": "~1.1.4" 261 | } 262 | }, 263 | "color-name": { 264 | "version": "1.1.4", 265 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 266 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 267 | "dev": true 268 | }, 269 | "command-line-args": { 270 | "version": "6.0.0", 271 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.0.tgz", 272 | "integrity": "sha512-zDdHxHzlCp/gA1gy0VtPK3YL0Aob3ijJdwZ7H3HSl55hh8EziLtRlyj/od8EGRJfX8IjussC/mQkScl2Ms5Suw==", 273 | "dev": true, 274 | "requires": { 275 | "array-back": "^6.2.2", 276 | "find-replace": "^5.0.1", 277 | "lodash.camelcase": "^4.3.0", 278 | "typical": "^7.1.1" 279 | } 280 | }, 281 | "command-line-usage": { 282 | "version": "7.0.3", 283 | "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", 284 | "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", 285 | "dev": true, 286 | "requires": { 287 | "array-back": "^6.2.2", 288 | "chalk-template": "^0.4.0", 289 | "table-layout": "^4.1.0", 290 | "typical": "^7.1.1" 291 | } 292 | }, 293 | "find-replace": { 294 | "version": "5.0.1", 295 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.1.tgz", 296 | "integrity": "sha512-o5/Y8HrCNRuFF5rdNTkX8Vhv6kTFTV0t1zIoigwlCdbkA9qaapRzxvWPND2VvlFa9LBI05Q1i8ml/saMqkOJUQ==", 297 | "dev": true, 298 | "requires": { 299 | "array-back": "^6.2.2" 300 | } 301 | }, 302 | "has-flag": { 303 | "version": "4.0.0", 304 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 305 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 306 | "dev": true 307 | }, 308 | "lodash.camelcase": { 309 | "version": "4.3.0", 310 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 311 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", 312 | "dev": true 313 | }, 314 | "minimist": { 315 | "version": "1.2.8", 316 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 317 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 318 | "dev": true 319 | }, 320 | "supports-color": { 321 | "version": "7.2.0", 322 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 323 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 324 | "dev": true, 325 | "requires": { 326 | "has-flag": "^4.0.0" 327 | } 328 | }, 329 | "table-layout": { 330 | "version": "4.1.1", 331 | "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", 332 | "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", 333 | "dev": true, 334 | "requires": { 335 | "array-back": "^6.2.2", 336 | "wordwrapjs": "^5.1.0" 337 | } 338 | }, 339 | "typical": { 340 | "version": "7.1.1", 341 | "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", 342 | "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", 343 | "dev": true 344 | }, 345 | "wordwrapjs": { 346 | "version": "5.1.0", 347 | "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", 348 | "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", 349 | "dev": true 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "command-line-commands", 3 | "author": "Lloyd Brookes <75pound@gmail.com>", 4 | "version": "4.0.1", 5 | "description": "Add a git-like command interface to your app.", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/75lb/command-line-commands.git" 9 | }, 10 | "license": "MIT", 11 | "files": [ 12 | "index.js", 13 | "option.js", 14 | "dist" 15 | ], 16 | "type": "module", 17 | "exports": { 18 | "import": "./index.js", 19 | "require": "./dist/index.cjs" 20 | }, 21 | "keywords": [ 22 | "argv", 23 | "parse", 24 | "argument", 25 | "args", 26 | "option", 27 | "parser", 28 | "parsing", 29 | "cli", 30 | "command", 31 | "commands", 32 | "line" 33 | ], 34 | "engines": { 35 | "node": ">=12.17" 36 | }, 37 | "scripts": { 38 | "test": "npm run dist && npm run test:ci", 39 | "test:ci": "75lb-nature test-runner test.js", 40 | "docs": "75lb-nature jsdoc2md -t README.hbs index.js > README.md", 41 | "dist": "75lb-nature cjs-build index.js" 42 | }, 43 | "peerDependencies": { 44 | "@75lb/nature": "latest" 45 | }, 46 | "peerDependenciesMeta": { 47 | "@75lb/nature": { 48 | "optional": true 49 | } 50 | }, 51 | "dependencies": { 52 | "array-back": "^6.2.2" 53 | }, 54 | "devDependencies": { 55 | "command-line-args": "^6.0.0", 56 | "command-line-usage": "^7.0.3", 57 | "minimist": "^1.2.8" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import commandLineCommands from 'command-line-commands' 2 | import { strict as a } from 'assert' 3 | 4 | const [test, only, skip] = [new Map(), new Map(), new Map()] 5 | 6 | /* Tests which parse process.argv expect this array */ 7 | process.argv = ['node', 'script', '--files', 'test.js'] 8 | 9 | test.set('simple', function () { 10 | const commands = [ 'eat', 'sleep' ] 11 | 12 | let clc = commandLineCommands(commands, [ 'eat', '--food', 'peas' ]) 13 | a.equal(clc.command, 'eat') 14 | a.deepEqual(clc.argv, [ '--food', 'peas' ]) 15 | 16 | clc = commandLineCommands(commands, [ 'sleep', '--hours', '2' ]) 17 | a.equal(clc.command, 'sleep') 18 | a.deepEqual(clc.argv, [ '--hours', '2' ]) 19 | }) 20 | 21 | test.set('no commands defined', function () { 22 | a.throws(function () { 23 | commandLineCommands([], [ 'eat' ]) 24 | }) 25 | a.throws(function () { 26 | commandLineCommands(undefined, [ 'eat' ]) 27 | }) 28 | a.throws(function () { 29 | commandLineCommands([]) 30 | }) 31 | a.throws(function () { 32 | commandLineCommands([], [ 'eat' ]) 33 | }) 34 | a.throws(function () { 35 | commandLineCommands() 36 | }) 37 | }) 38 | 39 | test.set('no command specified', function () { 40 | let clc 41 | let commands = [ ] 42 | 43 | /* throws if null not specified */ 44 | a.throws(function () { 45 | clc = commandLineCommands(commands, [ ]) 46 | }) 47 | 48 | /* null specified */ 49 | commands = [ null ] 50 | clc = commandLineCommands(commands, [ ]) 51 | a.equal(clc.command, null) 52 | a.deepEqual(clc.argv, [ ]) 53 | 54 | clc = commandLineCommands(commands, [ '--flag' ]) 55 | a.equal(clc.command, null) 56 | a.deepEqual(clc.argv, [ '--flag' ]) 57 | }) 58 | 59 | test.set('invalid command', function () { 60 | const commands = [ 'eat', 'sleep' ] 61 | let clc 62 | 63 | a.throws( 64 | function () { 65 | clc = commandLineCommands(commands, [ 'cheese', '--food', 'peas' ]) 66 | }, 67 | function (err) { 68 | return err.name === 'INVALID_COMMAND' && err.command === 'cheese' 69 | } 70 | ) 71 | }) 72 | 73 | test.set('parse process.argv', function () { 74 | const commands = [ null ] 75 | const clc = commandLineCommands(commands) 76 | a.equal(clc.command, null) 77 | a.deepEqual(clc.argv, [ '--files', 'test.js' ]) 78 | }) 79 | 80 | test.set('different types of option as the first arg', function () { 81 | const commands = [ null ] 82 | 83 | let clc = commandLineCommands(commands, [ '--one' ]) 84 | a.equal(clc.command, null) 85 | a.deepEqual(clc.argv, [ '--one' ]) 86 | 87 | clc = commandLineCommands(commands, [ '--one=two' ]) 88 | a.equal(clc.command, null) 89 | a.deepEqual(clc.argv, [ '--one=two' ]) 90 | 91 | clc = commandLineCommands(commands, [ '-o' ]) 92 | a.equal(clc.command, null) 93 | a.deepEqual(clc.argv, [ '-o' ]) 94 | 95 | clc = commandLineCommands(commands, [ '-of' ]) 96 | a.equal(clc.command, null) 97 | a.deepEqual(clc.argv, [ '-of' ]) 98 | }) 99 | 100 | export { test, only, skip } 101 | --------------------------------------------------------------------------------