Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,24 @@ jobs:
name: release
needs: test
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
issues: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 22
node-version: 24
- run: npm install

- name: Publish
run: npm run release
env:
GITHUB_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_AUTHOR_NAME: 'Dependant Bot'
GIT_AUTHOR_EMAIL: 'release-bot@codedependant.net'
GIT_COMMITTER_NAME: 'Dependant Bot'
GIT_COMMITTER_EMAIL: 'release-bot@codedependant.net'
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ cli.js

pnpm-lock.yaml
.tap
.tap-output/
.tap-out
.vimspector.json
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ name | required | type | description
**filter** | `false` | `function` | Receives the user input and return the filtered value to be used **inside** the program. The value returned will be added to the Answers hash.
**required_with** | `false` | `Array` | A non-empty array which says that if the flag is set, then the specified other flags must also be set, i.e. "mutual inclusion."
**required_without** | `false` | `Array` | A non-empty array which says that if the flag is set, then none of the other specified flags may also be set, i.e. "mutual exclusion."
**affirmative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `yes`)
**negative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `no`)

### Nested Flags

Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const logdna = require('eslint-config-logdna')
module.exports = defineConfig([
{
'extends': [logdna]
, 'files': ['lib/**/*.js', '*.js', 'test/**/*.js']
, 'files': ['lib/**/*.js', 'test/**/*.js', 'index.js']
, 'languageOptions': {
ecmaVersion: 2022
, sourceType: 'commonjs'
Expand Down
11 changes: 1 addition & 10 deletions examples/commands/hello.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ module.exports = new cli.Command({
'type': Boolean
, 'shorthand': 'e'
, 'description': 'Say hello in a very excited manner'
, 'default': true
, 'affirmative': 'Yes'
, 'negative': 'Nope'
, 'default': false
}

, volume: {
Expand All @@ -39,13 +37,6 @@ module.exports = new cli.Command({
, 'default': 'normal'
, 'shorthand': 'v'
}
, things: {
type: String
, description: 'which thing'
, choices: ['one', 'two', 'three']
, multi: true
, shorthand: 't'
}
}
, onContent: (content) => {
// command success
Expand Down
2 changes: 0 additions & 2 deletions examples/commands/sub/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ module.exports = new cli.Command({
'type': Boolean
, 'default': true
, 'description': 'do you like hub'
, affirmative: 'I think so'
, negative: 'i do not think so'
}
}
, async run(cmd, data) {
Expand Down
2 changes: 0 additions & 2 deletions gh-pages/guides/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,6 @@ name | required | type | description
**filter** | `false` | `function` | **interactive mode only** Receives the user input and return the filtered value to be used **inside** the program. The value returned will be added to the Answers hash.
**required_with** | `false` | `Array` | A non-empty array which says that if the flag is set, then the specified other flags must also be set, i.e. "mutual inclusion."
**required_without** | `false` | `Array` | A non-empty array which says that if the flag is set, then none of the other specified flags may also be set, i.e. "mutual exclusion."
**affirmative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `yes`)
**negative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `no`)


### Types
Expand Down
19 changes: 19 additions & 0 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"exclude": [
"deployment",
"compose"
],
"include": [
"lib/**/*.js",
"*.js"
],
"compilerOptions": {
"baseUrl": ".",
"moduleResolution": "Node",
"checkJs": false,
"target": "ES2025",
"module": "CommonJS",
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
}
}
25 changes: 14 additions & 11 deletions lib/command/flag-to-prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ const invertWhen = require('./invert-when')

module.exports = flagToPrompt

function toChoice(choice) {
return typeof choice === 'string' ? {name: choice, value: choice} : choice
}

function flagToPrompt(name, opt = {}) {
const display = name.replace(':', ' ')
const t = flagType(opt)

const flag_type = flagType(opt)
return {
type: t
, name: name
, message: display + ': ' + (opt.description || '(no description)')
, choices: opt.choices
, initial: opt.default || null
, skip: invertWhen(opt.when)
, result: opt.filter
, format: opt.transformer
, affirmative: opt.affirmative
, negative: opt.negative
'type': flag_type
, 'name': name
, 'message': display + ': ' + (opt.description || '(no description)')
, 'choices': Array.isArray(opt.choices) ? opt.choices.map(toChoice) : undefined
, 'default': opt.default || null
, 'skip': invertWhen(opt.when)
, 'filter': opt.filter
, 'transformer': opt.transformer
, 'mask': flag_type === 'password' ? true : undefined
}
}
9 changes: 2 additions & 7 deletions lib/command/flag-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@ function flagType(flag) {
, multi: true
})
}
if (flag.type === Boolean) {
if (flag.affirmative && flag.negative) {
return 'toggle'
}
return 'confirm'
}
if (flag.type === Boolean) return 'confirm'
if (flag.type === Number) return 'number'
if (flag.mask) return 'password'
if (flag.choices) {
if (flag.multi) return 'multiselect'
if (flag.multi) return 'checkbox'
return 'select'
}

Expand Down
123 changes: 123 additions & 0 deletions lib/command/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Base command class for creating interactive cli programs
* @module module:seeli/lib/command
* @author Eric Satterwhite
*/
declare class Command extends Map<any, any> {
/**
* Constructs and returns the final command usage
*/
get usage(): string;

/**
* The description of the command
*/
get description(): string;

/**
* The final parsed out command line input as key/value pairs
*/
get argv(): any;

/**
* Constructs and returns an object of flags and their types for consumption by the command
*/
get conf(): any;

/**
* Maps and returns any shorthand switches to their parent flags for consumption by the command
*/
get shorthands(): any;

/**
* Merges passing in object as configuration overrides
* @param options Configuration overrides to set
*/
setOptions(...opts: any[]): Command;

/**
* Dispatches an event for each flag that has the event flag enabled
*/
dispatch(): Command;

/**
* Method used to setup and execute the commands interactive mode
* @param cmd Optional argument for your command specific usage
* @param callback An optional callback to be executed when the command is complete.
*/
interactive(cmd?: any): Promise<any>;

/**
* Resets the internal command cache to its internal state
* @chainable
* @return Command
*/
reset(): Command;

/**
* Executes the command as defined
* @param cmd Optional argument for your command specific usage
* @param callback An optional callback to be executed when the command is complete.
* Will be passed the contents return by the command
* @return String|undefined Will return the result from the command specific run directive if there is any.
*/
run(cmd?: any, depth?: number): Promise<any>;

/**
* Validates the current data set before running the command
* @param command The name of the command being executed
*/
validate(command?: string): void;

/**
* Pass through function to inquirer for prompting input at the terminal
* @param options Inquirer prompt options
* @returns Promise object representing the end user input from the question
*/
prompt(options: any): Promise<any>;

/**
* Colorizes a text blob
* @param color The color to use. can be one of `red`, `blue`,`green`, `yellow`,`bold`, `grey`, `dim`, `black`, `magenta`, `cyan`
* @param text text to colorize
* @returns colorized version of the text
*/
colorize(color: string, text: string): string;

/**
* Registers a new sub command
* @param command The command to register
*/
use(command: Command): Command;

/**
* Convert all registered flags to inquierer compatible prompt objects
* @returns array of inquirer prompt objects
*/
toPrompt(): any[];

/**
* Get all available flags for this command
*/
get flags(): string[];

/**
* Get the command tree structure
*/
get tree(): any;

/**
* Run a command directly
* @param args Arguments to pass to the command
*/
static run(...args: any[]): Promise<any>;

/**
* Constructor for Command class
* @param options instance configuration
*/
constructor(...options: any[]);
}

export = Command;

Loading
Loading