Comparing version 0.0.2 to 0.0.3
v0.0.2 / 2019-06-03 | ||
v0.0.3 / 2020-05-25 | ||
================== | ||
* chore: clean code, correct README.md | ||
* chore: upgrade deps. | ||
* chore: adjust directory structure. | ||
* doc fix. | ||
v0.0.2 / 2019-06-03 | ||
=================== | ||
* Release v0.0.2 | ||
* file reference fix. | ||
@@ -6,0 +15,0 @@ |
150
lib/Cli.js
@@ -45,59 +45,3 @@ const Mri = require("mri"); | ||
} | ||
function parseCliCommand(argv, topLevelCommand, | ||
/** Matched command */ | ||
command) { | ||
// All added options | ||
const cliOptions = [ | ||
...topLevelCommand.options, | ||
...(command ? command.options : []) | ||
]; | ||
const mriOptions = Utils.getMriOptions(cliOptions); | ||
// Extract everything after `--` since mri doesn't support it | ||
let argsAfterDoubleDashes = []; | ||
const doubleDashesIndex = argv.indexOf('--'); | ||
if (doubleDashesIndex > -1) { | ||
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1); | ||
argv = argv.slice(0, doubleDashesIndex); | ||
} | ||
const parsed = Mri(argv, mriOptions); | ||
const args = parsed._; | ||
delete parsed._; | ||
const options = { | ||
'--': argsAfterDoubleDashes | ||
}; | ||
// Set option default value | ||
const ignoreDefault = command && command.config.ignoreOptionDefaultValue | ||
? command.config.ignoreOptionDefaultValue | ||
: topLevelCommand.config.ignoreOptionDefaultValue; | ||
let transforms = Object.create(null); | ||
for (const cliOption of cliOptions) { | ||
if (!ignoreDefault && cliOption.config.default !== undefined) { | ||
for (const name of cliOption.names) { | ||
options[name] = cliOption.config.default; | ||
} | ||
} | ||
// If options type is defined | ||
if (Array.isArray(cliOption.config.type)) { | ||
if (transforms[cliOption.name] === undefined) { | ||
transforms[cliOption.name] = Object.create(null); | ||
transforms[cliOption.name]['shouldTransform'] = true; | ||
transforms[cliOption.name]['transformFunction'] = cliOption.config.type[0]; | ||
} | ||
} | ||
} | ||
// Camelcase option names and set dot nested option values | ||
for (const key of Object.keys(parsed)) { | ||
const keys = key.split('.').map((v, i) => { | ||
return i === 0 ? Utils.camelCase(v) : v; | ||
}); | ||
Utils.setDotProp(options, keys, parsed[key]); | ||
Utils.setByType(options, transforms); | ||
} | ||
return { | ||
args, | ||
options, | ||
rawOptions: parsed | ||
}; | ||
} | ||
module.exports = class FCli extends events_1.EventEmitter { | ||
class FCli extends events_1.EventEmitter { | ||
/** | ||
@@ -125,3 +69,3 @@ * @param name The program name to display in help and version message | ||
command(raw, description, config) { | ||
const command = new Command_1.FCliCommand(raw, description || '', config, this); | ||
const command = new Command_1.CliCommand(raw, description || '', config, this); | ||
command.topLevelCommand = this.topLevelCommand; | ||
@@ -186,3 +130,3 @@ this.commands.push(command); | ||
shouldParseDefault = false; | ||
const parsedInfo = Object.assign({}, parsedCmdResult, { args: parsedCmdResult.args.slice(1) }); | ||
const parsedInfo = Object.assign(Object.assign({}, parsedCmdResult), { args: parsedCmdResult.args.slice(1) }); | ||
setParsedInfo.call(this, parsedInfo, command, commandName); | ||
@@ -239,2 +183,88 @@ this.emit(`command:${commandName}`, command); | ||
} | ||
}; | ||
} | ||
function getMriOptions(options) { | ||
const result = { alias: {}, boolean: [] }; | ||
for (const [index, option] of options.entries()) { | ||
// We do not set default values in mri options | ||
// Since its type (typeof) will be used to cast parsed arguments. | ||
// Which mean `--foo foo` will be parsed as `{foo: true}` if we have `{default:{foo: true}}` | ||
// Set alias | ||
if (option.names.length > 1) { | ||
result.alias[option.names[0]] = option.names.slice(1); | ||
} | ||
// Set boolean | ||
if (option.isBoolean) { | ||
if (option.negative) { | ||
// For negative option | ||
// We only set it to `boolean` type when there's no string-type option with the same name | ||
const hasStringTypeOption = options.some((o, i) => { | ||
return (i !== index && | ||
typeof o.required === 'boolean' && | ||
o.names.some(name => option.names.includes(name))); | ||
}); | ||
if (!hasStringTypeOption) | ||
result.boolean.push(option.names[0]); | ||
} | ||
else | ||
result.boolean.push(option.names[0]); | ||
} | ||
} | ||
return result; | ||
} | ||
function parseCliCommand(argv, topLevelCommand, | ||
/** Matched command */ | ||
command) { | ||
// All added options | ||
const cliOptions = [ | ||
...topLevelCommand.options, | ||
...(command ? command.options : []) | ||
]; | ||
const mriOptions = getMriOptions(cliOptions); | ||
// Extract everything after `--` since mri doesn't support it | ||
let argsAfterDoubleDashes = []; | ||
const doubleDashesIndex = argv.indexOf('--'); | ||
if (doubleDashesIndex > -1) { | ||
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1); | ||
argv = argv.slice(0, doubleDashesIndex); | ||
} | ||
const parsed = Mri(argv, mriOptions); | ||
const args = parsed._; | ||
delete parsed._; | ||
const options = { | ||
'--': argsAfterDoubleDashes | ||
}; | ||
// Set option default value | ||
const ignoreDefault = command && command.config.ignoreOptionDefaultValue | ||
? command.config.ignoreOptionDefaultValue | ||
: topLevelCommand.config.ignoreOptionDefaultValue; | ||
let transforms = Object.create(null); | ||
for (const cliOption of cliOptions) { | ||
if (!ignoreDefault && cliOption.config.default !== undefined) { | ||
for (const name of cliOption.names) { | ||
options[name] = cliOption.config.default; | ||
} | ||
} | ||
// If options type is defined | ||
if (Array.isArray(cliOption.config.type)) { | ||
if (transforms[cliOption.name] === undefined) { | ||
transforms[cliOption.name] = Object.create(null); | ||
transforms[cliOption.name]['shouldTransform'] = true; | ||
transforms[cliOption.name]['transformFunction'] = cliOption.config.type[0]; | ||
} | ||
} | ||
} | ||
// Camelcase option names and set dot nested option values | ||
for (const key of Object.keys(parsed)) { | ||
const keys = key.split('.').map((v, i) => { | ||
return i === 0 ? Utils.camelCase(v) : v; | ||
}); | ||
Utils.setDotProp(options, keys, parsed[key]); | ||
Utils.setByType(options, transforms); | ||
} | ||
return { | ||
args, | ||
options, | ||
rawOptions: parsed | ||
}; | ||
} | ||
module.exports = FCli; |
@@ -0,7 +1,9 @@ | ||
/// <reference path="../dts/command.d.ts" /> | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Option = require("./Option"); | ||
exports.FCliGlobalCommand = exports.CliCommand = void 0; | ||
const CliOption = require("./Option"); | ||
const Utils = require("./utils"); | ||
const process_1 = require("process"); | ||
const ctrl_1 = require("./ctrl"); | ||
class FCliCommand { | ||
class CliCommand { | ||
constructor(raw, description, config = {}, cli) { | ||
@@ -18,2 +20,7 @@ this.raw = raw; | ||
} | ||
/** | ||
* set usage text | ||
* | ||
* @param text usage text | ||
*/ | ||
usage(text) { | ||
@@ -23,2 +30,8 @@ this.usageText = text; | ||
} | ||
/** | ||
* set version number | ||
* | ||
* @param version semver string | ||
* @param customFlags customzied flags, default as `-v, --version` | ||
*/ | ||
version(version, customFlags = '-v, --version') { | ||
@@ -29,2 +42,7 @@ this.versionNumber = version; | ||
} | ||
/** | ||
* Add command example | ||
* | ||
* @param example Example Instance | ||
*/ | ||
example(example) { | ||
@@ -41,5 +59,9 @@ this.examples.push(example); | ||
option(raw, description, config) { | ||
this.options.push(new Option(raw, description, config)); | ||
this.options.push(new CliOption(raw, description, config)); | ||
return this; | ||
} | ||
/** | ||
* add alias of this command | ||
* @param name alias name | ||
*/ | ||
alias(name) { | ||
@@ -49,2 +71,17 @@ this.aliasNames.push(name); | ||
} | ||
/** | ||
* set action for this command | ||
* | ||
* | ||
interface ActionCallback { | ||
( | ||
// Parsed CLI args | ||
// The last arg will be an array if it's an varadic argument | ||
...args: string | string[] | number | number[], | ||
// Parsed CLI options | ||
options: CliOption[] | ||
): void | ||
} | ||
* @param callback callback when this command executed | ||
*/ | ||
action(callback) { | ||
@@ -198,4 +235,4 @@ Utils.addHiddenChangeableProperty(this, 'commandAction', callback); | ||
} | ||
exports.FCliCommand = FCliCommand; | ||
class FCliGlobalCommand extends FCliCommand { | ||
exports.CliCommand = CliCommand; | ||
class FCliGlobalCommand extends CliCommand { | ||
constructor(cli) { | ||
@@ -202,0 +239,0 @@ super('@@global@@', '', {}, cli); |
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.PLATFORM_INFO = exports.EOL = exports.DEFAULT_ARGS = exports.exit = void 0; | ||
exports.exit = (code) => { | ||
@@ -3,0 +4,0 @@ return process.exit(code); |
const FCli = require("./Cli"); | ||
const FN = (...args) => new FCli(...args); | ||
module.exports = FN; | ||
module.exports = (...args) => new FCli(...args); |
const Utils = require("./utils"); | ||
module.exports = class FCliOption { | ||
class CliOption { | ||
constructor(raw, description, config) { | ||
this.raw = raw; | ||
this.description = description; | ||
this.config = {}; | ||
this.config = Object.assign({}, config); | ||
@@ -33,2 +34,3 @@ // You may use cli.option('--env.* [value]', 'desc') to denote a dot-nested option | ||
} | ||
}; | ||
} | ||
module.exports = CliOption; |
@@ -0,3 +1,34 @@ | ||
// interface ParserOptions { | ||
// string?: string | string[] | ||
// boolean?: boolean | string | string[] | ||
// /** | ||
// * @example | ||
// * | ||
// * { | ||
// * "alias1": "argument1", | ||
// * "alias2": "argument2", | ||
// * } | ||
// * | ||
// * or | ||
// * | ||
// * ["argument1", "argument2"] | ||
// */ | ||
// alias?: { [a: string]: string } | string[] | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// export function scan(argv: string | string[], opts: FCliCommon.ParserOptions) { | ||
exports.addHiddenChangeableProperty = exports.addVisibleUnWrittableProperty = exports.addUnWrittableProperty = exports.getProgramAppFromFilepath = exports.setByType = exports.setDotProp = exports.camelCase = exports.padRight = exports.findLongestStr = exports.parseBracketedArgs = exports.removeBrackets = void 0; | ||
// /** | ||
// * @example | ||
// * | ||
// * { | ||
// * "arg1": [defaultValue], | ||
// * "arg2": [defaultValue], | ||
// * } | ||
// */ | ||
// default?: { [arg: string]: any } | ||
// unknown?: { | ||
// (k: string): boolean | ||
// } | ||
// '--': boolean | ||
// } | ||
// export function scan(argv: string | string[], opts: FCliCommonNS.ParserOptions) { | ||
// if (!Array.isArray(argv)) | ||
@@ -21,3 +52,3 @@ // argv = [argv] | ||
// type LOGLEVEL = 'error' | 'warn' | 'log' | ||
// function checkOptions(opts: FCliCommon.ParserOptions): undefined | [string, LOGLEVEL, boolean] { | ||
// function checkOptions(opts: FCliCommonNS.ParserOptions): undefined | [string, LOGLEVEL, boolean] { | ||
// if (!opts) | ||
@@ -80,32 +111,2 @@ // return | ||
exports.parseBracketedArgs = parseBracketedArgs; | ||
function getMriOptions(options) { | ||
const result = { alias: {}, boolean: [] }; | ||
for (const [index, option] of options.entries()) { | ||
// We do not set default values in mri options | ||
// Since its type (typeof) will be used to cast parsed arguments. | ||
// Which mean `--foo foo` will be parsed as `{foo: true}` if we have `{default:{foo: true}}` | ||
// Set alias | ||
if (option.names.length > 1) { | ||
result.alias[option.names[0]] = option.names.slice(1); | ||
} | ||
// Set boolean | ||
if (option.isBoolean) { | ||
if (option.negative) { | ||
// For negative option | ||
// We only set it to `boolean` type when there's no string-type option with the same name | ||
const hasStringTypeOption = options.some((o, i) => { | ||
return (i !== index && | ||
typeof o.required === 'boolean' && | ||
o.names.some(name => option.names.includes(name))); | ||
}); | ||
if (!hasStringTypeOption) | ||
result.boolean.push(option.names[0]); | ||
} | ||
else | ||
result.boolean.push(option.names[0]); | ||
} | ||
} | ||
return result; | ||
} | ||
exports.getMriOptions = getMriOptions; | ||
function findLongestStr(arr) { | ||
@@ -112,0 +113,0 @@ return arr.sort((a, b) => { |
{ | ||
"name": "@fxjs/cli", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "", | ||
"types": "@types/index.d.ts", | ||
"types": "typings/index.d.ts", | ||
"main": "lib", | ||
"scripts": { | ||
"build": "fib-typify src -o lib", | ||
"build": "ftsc src/* --outDir ./lib", | ||
"test": "fibjs test", | ||
@@ -18,18 +18,23 @@ "ci": "npm run build && npm run test", | ||
"0.26.1", | ||
"0.27.0" | ||
"0.27.0", | ||
"0.28.0", | ||
"0.29.0", | ||
"0.30.0" | ||
] | ||
}, | ||
"author": "richardo2016@gmail.com", | ||
"author": "Richard <richardo2016@gmail.com>", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@fibjs/ci": "^2.2.0", | ||
"@fibjs/types": "^0.27.0", | ||
"fib-typify": "^0.5.2" | ||
}, | ||
"dependencies": { | ||
"@fibjs/detect-port": "^1.0.2", | ||
"@fibjs/mkdirp": "^1.0.1", | ||
"@richardo2016/ts-type-helpers": "^0.1.4", | ||
"debug": "^4.1.1", | ||
"mri": "^1.1.4" | ||
}, | ||
"devDependencies": { | ||
"@fibjs/ci": "^2.2.0", | ||
"@fibjs/types": "^0.27.0", | ||
"@types/mri": "^1.1.0", | ||
"fib-typify": "^0.8.2" | ||
} | ||
} |
@@ -50,3 +50,2 @@ [![NPM version](https://img.shields.io/npm/v/@fxjs/cli.svg?style=flat)](https://npmjs.com/package/@fxjs/cli) | ||
- [command.alias(name)](#commandaliasname) | ||
- [command.allowUnknownOptions()](#commandallowunknownoptions) | ||
- [command.example(example)](#commandexampleexample) | ||
@@ -136,6 +135,13 @@ - [Events](#events) | ||
A command's options are validated when the command is used. Any unknown options will be reported as an error. However, if an action-based command does not define an action, then the options are not validated. If you really want to use unknown options, use [`command.allowUnknownOptions`](#commandallowunknownoptions). | ||
A command's options are validated when the command is used. Any unknown options will be reported as an error by default. However, if an action-based command does not define an action, then the options are not validated. If you really want to use unknown options, pass `allowUnknownOptions: true` when initializing the command, like this: | ||
<img src="https://user-images.githubusercontent.com/6339390/58762857-9ac19300-8586-11e9-98c9-12add42e1d3a.png" alt="image" style="max-width:100%;"> | ||
```js | ||
const cli = require('@fxjs/cli')() | ||
cli | ||
.command('rm <dir>', 'Remove a dir', { | ||
allowUnknownOptions: true | ||
}) | ||
``` | ||
### Brackets | ||
@@ -142,0 +148,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
49620
20
984
439
5
4
+ Added@richardo2016/ts-type-helpers@0.1.4(transitive)