Skip to content

Commit b51adbe

Browse files
committed
fix: unknown options should be checked against original cli options
1 parent e51fe6e commit b51adbe

File tree

6 files changed

+95
-48
lines changed

6 files changed

+95
-48
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"devDependencies": {
2525
"@types/execa": "^0.9.0",
2626
"@types/jest": "^23.3.9",
27+
"@types/minimist": "^1.2.0",
2728
"cz-conventional-changelog": "^2.1.0",
2829
"eslint-config-rem": "^3.0.0",
2930
"execa": "^1.0.0",
@@ -38,7 +39,7 @@
3839
"typescript": "^3.1.6"
3940
},
4041
"dependencies": {
41-
"minimost": "^1.2.0"
42+
"minimist": "^1.2.0"
4243
},
4344
"release": {
4445
"branch": "master"

src/Command.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ export default class Command {
212212
return this
213213
}
214214

215+
/**
216+
* Check if the parsed options contain any unknown options
217+
* Exit and output error when true
218+
* @param options Original options, i.e. not camelCased one
219+
* @param globalCommand
220+
*/
215221
checkUnknownOptions(options: { [k: string]: any }, globalCommand: Command) {
216222
if (!this.config.allowUnknownOptions) {
217223
for (const name of Object.keys(options)) {
@@ -225,12 +231,10 @@ export default class Command {
225231
name.length > 1 ? `--${name}` : `-${name}`
226232
}\``
227233
)
228-
process.exitCode = 1
229-
return true
234+
process.exit(1)
230235
}
231236
}
232237
}
233-
return false
234238
}
235239
}
236240

src/index.ts

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { EventEmitter } from 'events'
22
import path from 'path'
3-
import minimost, { Opts as MinimostOpts } from 'minimost'
3+
import minimist, { Opts as MinimistOpts } from 'minimist'
44
import Command, { HelpCallback, CommandExample } from './Command'
55
import { OptionConfig } from './Option'
6-
import { getMinimostOptions } from './utils'
6+
import { getMinimistOptions, camelcase } from './utils'
77

88
interface ParsedArgv {
99
args: string[]
@@ -12,6 +12,16 @@ interface ParsedArgv {
1212
}
1313
}
1414

15+
interface MinimistResult extends ParsedArgv {
16+
args: string[]
17+
options: {
18+
[k: string]: any
19+
}
20+
originalOptions: {
21+
[k: string]: any
22+
}
23+
}
24+
1525
const NAME_OF_GLOBAL_COMMAND = 'this-does-not-matter'
1626

1727
class CAC extends EventEmitter {
@@ -28,7 +38,7 @@ class CAC extends EventEmitter {
2838
*/
2939
args: string[]
3040
/**
31-
* Parsed CLI options
41+
* Parsed CLI options, camelCased
3242
*/
3343
options: { [k: string]: any }
3444

@@ -106,75 +116,110 @@ class CAC extends EventEmitter {
106116
this.bin = argv[1] ? path.basename(argv[1]) : 'cli'
107117

108118
for (const command of this.commands) {
109-
const minimostOptions = getMinimostOptions([
119+
const minimistOptions = getMinimistOptions([
110120
...this.globalCommand.options,
111121
...command.options
112122
])
113-
const parsed = this.minimost(argv.slice(2), minimostOptions)
114-
if (command.isMatched(parsed.args[0])) {
123+
const { args, options, originalOptions } = this.minimist(
124+
argv.slice(2),
125+
minimistOptions
126+
)
127+
if (command.isMatched(args[0])) {
115128
this.matchedCommand = command
116-
this.args = parsed.args
117-
this.options = parsed.options
118-
this.emit(`command:${parsed.args[0]}`, command)
119-
this.runCommandAction(command, this.globalCommand, parsed)
120-
return parsed
129+
this.args = args
130+
this.options = options
131+
this.emit(`command:${args[0]}`, command)
132+
this.runCommandAction(command, this.globalCommand, {
133+
args,
134+
options,
135+
originalOptions
136+
})
137+
return { args, options }
121138
}
122139
}
123140

124141
// Try the default command
125142
for (const command of this.commands) {
126143
if (command.name === '') {
127-
const minimostOptions = getMinimostOptions([
144+
const minimistOptions = getMinimistOptions([
128145
...this.globalCommand.options,
129146
...command.options
130147
])
131-
const parsed = this.minimost(argv.slice(2), minimostOptions)
132-
this.args = parsed.args
133-
this.options = parsed.options
148+
const { args, options, originalOptions } = this.minimist(
149+
argv.slice(2),
150+
minimistOptions
151+
)
134152
this.matchedCommand = command
153+
this.args = args
154+
this.options = options
135155
this.emit(`command:!`, command)
136-
this.runCommandAction(command, this.globalCommand, parsed)
137-
return parsed
156+
this.runCommandAction(command, this.globalCommand, {
157+
args,
158+
options,
159+
originalOptions
160+
})
161+
return { args, options }
138162
}
139163
}
140164

141-
const globalMinimostOptions = getMinimostOptions(this.globalCommand.options)
142-
const parsed = this.minimost(argv.slice(2), globalMinimostOptions)
143-
this.args = parsed.args
144-
this.options = parsed.options
165+
const globalMinimistOptions = getMinimistOptions(this.globalCommand.options)
166+
const { args, options } = this.minimist(
167+
argv.slice(2),
168+
globalMinimistOptions
169+
)
170+
this.args = args
171+
this.options = options
145172

146-
if (parsed.options.help && this.globalCommand.hasOption('help')) {
173+
if (options.help && this.globalCommand.hasOption('help')) {
147174
this.outputHelp()
148-
return parsed
149175
}
150176

151177
if (
152-
parsed.options.version &&
178+
options.version &&
153179
this.globalCommand.hasOption('version') &&
154180
this.globalCommand.versionNumber
155181
) {
156182
this.outputVersion()
157-
return parsed
158183
}
159184

160185
this.emit('command:*')
161186

162-
return parsed
187+
return { args, options }
163188
}
164189

165-
minimost(argv: string[], minimostOptions: MinimostOpts) {
166-
const { input: args, flags: options } = minimost(argv, minimostOptions)
190+
private minimist(
191+
argv: string[],
192+
minimistOptions: MinimistOpts
193+
): MinimistResult {
194+
const parsed = minimist(
195+
argv,
196+
Object.assign(
197+
{
198+
'--': true
199+
},
200+
minimistOptions
201+
)
202+
)
203+
204+
const args = parsed._
205+
delete parsed._
206+
207+
const options: { [k: string]: any } = {}
208+
for (const key of Object.keys(parsed)) {
209+
options[camelcase(key)] = parsed[key]
210+
}
167211

168212
return {
169213
args,
170-
options
214+
options,
215+
originalOptions: parsed
171216
}
172217
}
173218

174-
runCommandAction(
219+
private runCommandAction(
175220
command: Command,
176221
globalCommand: Command,
177-
{ args, options }: ParsedArgv
222+
{ args, options, originalOptions }: MinimistResult
178223
) {
179224
if (options.help && globalCommand.hasOption('help')) {
180225
return this.outputHelp(true)
@@ -186,7 +231,7 @@ class CAC extends EventEmitter {
186231

187232
if (!command.commandAction) return
188233

189-
if (command.checkUnknownOptions(options, globalCommand)) return
234+
command.checkUnknownOptions(originalOptions, globalCommand)
190235

191236
// The first one is command name
192237
if (!command.isDefaultCommand) {
@@ -199,8 +244,7 @@ class CAC extends EventEmitter {
199244
console.error(
200245
`error: missing required args for command "${command.rawName}"`
201246
)
202-
process.exitCode = 1
203-
return
247+
process.exit(1)
204248
}
205249

206250
const actionArgs: any[] = []

src/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const findAllBrackets = (v: string) => {
3535
return res
3636
}
3737

38-
export const getMinimostOptions = (options: Option[]) => {
38+
export const getMinimistOptions = (options: Option[]) => {
3939
return {
4040
default: options.reduce((res: { [k: string]: any }, option) => {
4141
if (option.config.default !== undefined) {
@@ -68,3 +68,9 @@ export const findLongest = (arr: string[]) => {
6868
export const padRight = (str: string, length: number) => {
6969
return str.length >= length ? str : `${str}${' '.repeat(length - str.length)}`
7070
}
71+
72+
export const camelcase = (input: string) => {
73+
return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {
74+
return p1 + p2.toUpperCase()
75+
})
76+
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
"module": "commonjs",
2222
"outDir": "dist"
2323
},
24-
"include": ["src"]
24+
"include": ["src", "declarations.d.ts"]
2525
}

yarn.lock

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,14 +4337,6 @@ minimist@~0.0.1:
43374337
resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
43384338
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
43394339

4340-
minimost@^1.2.0:
4341-
version "1.2.0"
4342-
resolved "https://registry.npmjs.org/minimost/-/minimost-1.2.0.tgz#a37f91d60395fc003180d208ca9e0316bcc4e3a2"
4343-
integrity sha512-/+eWyOtXw41WIUV9rBgrXna11bxbqymebSeW2arsfp/MCGCwe+2czzsOueEtLZgH4xb4QXhje5H9MLCsCPibLA==
4344-
dependencies:
4345-
"@types/minimist" "^1.2.0"
4346-
minimist "^1.2.0"
4347-
43484340
minipass@^2.2.1, minipass@^2.3.3, minipass@^2.3.4:
43494341
version "2.3.5"
43504342
resolved "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"

0 commit comments

Comments
 (0)