Skip to content

Reusable structured flags #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 23, 2023
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
34 changes: 8 additions & 26 deletions lib/commands/info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chalk from 'chalk'
import meow from 'meow'
import ora from 'ora'

import { outputFlags, validationFlags } from '../../flags/index.js'
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
import { InputError } from '../../utils/errors.js'
Expand Down Expand Up @@ -47,17 +48,17 @@ export const info = {
* @returns {void|CommandContext}
*/
function setupCommand (name, description, argv, importMeta) {
const flags = {
...outputFlags,
...validationFlags,
}

const cli = meow(`
Usage
$ ${name} <name>

Options
${printFlagList({
'--all': 'Include all issues',
'--json': 'Output result as json',
'--markdown': 'Output result as markdown',
'--strict': 'Exits with an error code if any matching issues are found',
}, 6)}
${printFlagList(flags, 6)}

Examples
$ ${name} webtorrent
Expand All @@ -66,26 +67,7 @@ function setupCommand (name, description, argv, importMeta) {
argv,
description,
importMeta,
flags: {
all: {
type: 'boolean',
default: false,
},
json: {
type: 'boolean',
alias: 'j',
default: false,
},
markdown: {
type: 'boolean',
alias: 'm',
default: false,
},
strict: {
type: 'boolean',
default: false,
},
}
flags
})

const {
Expand Down
59 changes: 25 additions & 34 deletions lib/commands/report/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import ora from 'ora'
import { ErrorWithCause } from 'pony-cause'

import { fetchReportData, formatReportDataOutput } from './view.js'
import { outputFlags, validationFlags } from '../../flags/index.js'
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
import { ChalkOrMarkdown, logSymbols } from '../../utils/chalk-markdown.js'
import { InputError } from '../../utils/errors.js'
import { prepareFlags } from '../../utils/flags.js'
import { printFlagList } from '../../utils/formatting.js'
import { createDebugLogger } from '../../utils/misc.js'
import { getPackageFiles } from '../../utils/path-resolve.js'
Expand Down Expand Up @@ -79,6 +81,28 @@ export const create = {
* @returns {Promise<void|CommandContext>}
*/
async function setupCommand (name, description, argv, importMeta) {
const flags = prepareFlags({
...outputFlags,
...validationFlags,
debug: {
type: 'boolean',
alias: 'd',
default: false,
description: 'Output debug information',
},
dryRun: {
type: 'boolean',
default: false,
description: 'Only output what will be done without actually doing it',
},
view: {
type: 'boolean',
alias: 'v',
default: false,
description: 'Will wait for and return the created report'
},
})

const cli = meow(`
Usage
$ ${name} <paths-to-package-folders-and-files>
Expand Down Expand Up @@ -114,40 +138,7 @@ async function setupCommand (name, description, argv, importMeta) {
argv,
description,
importMeta,
flags: {
all: {
type: 'boolean',
default: false,
},
debug: {
type: 'boolean',
alias: 'd',
default: false,
},
dryRun: {
type: 'boolean',
default: false,
},
json: {
type: 'boolean',
alias: 'j',
default: false,
},
markdown: {
type: 'boolean',
alias: 'm',
default: false,
},
strict: {
type: 'boolean',
default: false,
},
view: {
type: 'boolean',
alias: 'v',
default: false,
},
}
flags,
})

const {
Expand Down
35 changes: 8 additions & 27 deletions lib/commands/report/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chalk from 'chalk'
import meow from 'meow'
import ora from 'ora'

import { outputFlags, validationFlags } from '../../flags/index.js'
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
import { InputError } from '../../utils/errors.js'
Expand All @@ -28,7 +29,6 @@ export const view = {

// Internal functions

// TODO: Share more of the flag setup inbetween the commands
/**
* @typedef CommandContext
* @property {boolean} includeAllIssues
Expand All @@ -46,44 +46,25 @@ export const view = {
* @returns {void|CommandContext}
*/
function setupCommand (name, description, argv, importMeta) {
const flags = {
...outputFlags,
...validationFlags,
}

const cli = meow(`
Usage
$ ${name} <report-identifier>

Options
${printFlagList({
'--all': 'Include all issues',
'--json': 'Output result as json',
'--markdown': 'Output result as markdown',
'--strict': 'Exits with an error code if report result is deemed unhealthy',
}, 6)}
${printFlagList(flags, 6)}

Examples
$ ${name} QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
`, {
argv,
description,
importMeta,
flags: {
all: {
type: 'boolean',
default: false,
},
json: {
type: 'boolean',
alias: 'j',
default: false,
},
markdown: {
type: 'boolean',
alias: 'm',
default: false,
},
strict: {
type: 'boolean',
default: false,
},
}
flags,
})

// Extract the input
Expand Down
2 changes: 2 additions & 0 deletions lib/flags/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { outputFlags } from './output.js'
export { validationFlags } from './validation.js'
16 changes: 16 additions & 0 deletions lib/flags/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { prepareFlags } from '../utils/flags.js'

export const outputFlags = prepareFlags({
json: {
type: 'boolean',
alias: 'j',
default: false,
description: 'Output result as json',
},
markdown: {
type: 'boolean',
alias: 'm',
default: false,
description: 'Output result as markdown',
},
})
14 changes: 14 additions & 0 deletions lib/flags/validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { prepareFlags } from '../utils/flags.js'

export const validationFlags = prepareFlags({
all: {
type: 'boolean',
default: false,
description: 'Include all issues',
},
strict: {
type: 'boolean',
default: false,
description: 'Exits with an error code if any matching issues are found',
},
})
27 changes: 27 additions & 0 deletions lib/utils/flags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @typedef FlagExtensions
* @property {string} description
*/

/**
* @template {import('meow').FlagType} Type
* @template Default
* @template {boolean} [IsMultiple=false]
* @typedef {import('meow').Flag<Type, Default, IsMultiple> & FlagExtensions} Flag
*/

/** @typedef {Flag<'string', string> | Flag<'string', string[], true>} StringFlag */
/** @typedef {Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>} BooleanFlag */
/** @typedef {Flag<'number', number> | Flag<'number', number[], true>} NumberFlag */
/** @typedef {StringFlag | BooleanFlag | NumberFlag} AnyFlag */
/** @typedef {Record<string, AnyFlag>} AnyFlags */

/**
* @template {AnyFlags} Flags
* @param {Flags} flags
* @returns {Readonly<Flags>}
*/
export function prepareFlags (flags) {
// As we can't do "satisfies AnyFlags" in JS yet (+ we add a bonus through Readonly<>)
return flags
}
29 changes: 20 additions & 9 deletions lib/utils/formatting.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
/** @typedef {string|{ description: string }} ListDescription */

/**
* @typedef HelpListOptions
* @property {string} [keyPrefix]
* @property {number} [padName]
*/

/**
* @param {Record<string,ListDescription>} list
* @param {number} indent
* @param {number} padName
* @param {HelpListOptions} options
* @returns {string}
*/
export function printHelpList (list, indent, padName = 18) {
export function printHelpList (list, indent, options = {}) {
const {
keyPrefix = '',
padName = 18,
} = options

const names = Object.keys(list).sort()

let result = ''
Expand All @@ -15,22 +26,22 @@ export function printHelpList (list, indent, padName = 18) {
const rawDescription = list[name]
const description = (typeof rawDescription === 'object' ? rawDescription.description : rawDescription) || ''

result += ''.padEnd(indent) + name.padEnd(padName) + description + '\n'
result += ''.padEnd(indent) + (keyPrefix + name).padEnd(padName) + description + '\n'
}

return result.trim()
}

/**
* @param {Record<string,ListDescription>} list
* @param {Record<string, ListDescription>} list
* @param {number} indent
* @param {number} padName
* @param {HelpListOptions} options
* @returns {string}
*/
export function printFlagList (list, indent, padName = 18) {
export function printFlagList (list, indent, options = {}) {
return printHelpList({
'--help': 'Print this help and exits.',
'--version': 'Prints current version and exits.',
'help': 'Print this help and exits.',
'version': 'Prints current version and exits.',
...list,
}, indent, padName)
}, indent, { keyPrefix: '--', ...options })
}