2023-04-21 14:57:54 +00:00
import { argsert } from './argsert.js' ;
import { assertNotStrictEqual , } from './typings/common-types.js' ;
import { levenshtein as distance } from './utils/levenshtein.js' ;
import { objFilter } from './utils/obj-filter.js' ;
const specialKeys = [ '$0' , '--' , '_' ] ;
export function validation ( yargs , usage , shim ) {
const _ _ = shim . y18n . _ _ ;
const _ _n = shim . y18n . _ _n ;
const self = { } ;
self . nonOptionCount = function nonOptionCount ( argv ) {
const demandedCommands = yargs . getDemandedCommands ( ) ;
const positionalCount = argv . _ . length + ( argv [ '--' ] ? argv [ '--' ] . length : 0 ) ;
const _s = positionalCount - yargs . getInternalMethods ( ) . getContext ( ) . commands . length ;
if ( demandedCommands . _ &&
( _s < demandedCommands . _ . min || _s > demandedCommands . _ . max ) ) {
if ( _s < demandedCommands . _ . min ) {
if ( demandedCommands . _ . minMsg !== undefined ) {
usage . fail ( demandedCommands . _ . minMsg
? demandedCommands . _ . minMsg
. replace ( /\$0/g , _s . toString ( ) )
. replace ( /\$1/ , demandedCommands . _ . min . toString ( ) )
: null ) ;
}
else {
usage . fail ( _ _n ( 'Not enough non-option arguments: got %s, need at least %s' , 'Not enough non-option arguments: got %s, need at least %s' , _s , _s . toString ( ) , demandedCommands . _ . min . toString ( ) ) ) ;
}
}
else if ( _s > demandedCommands . _ . max ) {
if ( demandedCommands . _ . maxMsg !== undefined ) {
usage . fail ( demandedCommands . _ . maxMsg
? demandedCommands . _ . maxMsg
. replace ( /\$0/g , _s . toString ( ) )
. replace ( /\$1/ , demandedCommands . _ . max . toString ( ) )
: null ) ;
}
else {
usage . fail ( _ _n ( 'Too many non-option arguments: got %s, maximum of %s' , 'Too many non-option arguments: got %s, maximum of %s' , _s , _s . toString ( ) , demandedCommands . _ . max . toString ( ) ) ) ;
}
}
}
} ;
self . positionalCount = function positionalCount ( required , observed ) {
if ( observed < required ) {
usage . fail ( _ _n ( 'Not enough non-option arguments: got %s, need at least %s' , 'Not enough non-option arguments: got %s, need at least %s' , observed , observed + '' , required + '' ) ) ;
}
} ;
self . requiredArguments = function requiredArguments ( argv , demandedOptions ) {
let missing = null ;
for ( const key of Object . keys ( demandedOptions ) ) {
if ( ! Object . prototype . hasOwnProperty . call ( argv , key ) ||
typeof argv [ key ] === 'undefined' ) {
missing = missing || { } ;
missing [ key ] = demandedOptions [ key ] ;
}
}
if ( missing ) {
const customMsgs = [ ] ;
for ( const key of Object . keys ( missing ) ) {
const msg = missing [ key ] ;
if ( msg && customMsgs . indexOf ( msg ) < 0 ) {
customMsgs . push ( msg ) ;
}
}
const customMsg = customMsgs . length ? ` \n ${ customMsgs . join ( '\n' ) } ` : '' ;
usage . fail ( _ _n ( 'Missing required argument: %s' , 'Missing required arguments: %s' , Object . keys ( missing ) . length , Object . keys ( missing ) . join ( ', ' ) + customMsg ) ) ;
}
} ;
self . unknownArguments = function unknownArguments ( argv , aliases , positionalMap , isDefaultCommand , checkPositionals = true ) {
var _a ;
const commandKeys = yargs
. getInternalMethods ( )
. getCommandInstance ( )
. getCommands ( ) ;
const unknown = [ ] ;
const currentContext = yargs . getInternalMethods ( ) . getContext ( ) ;
Object . keys ( argv ) . forEach ( key => {
if ( ! specialKeys . includes ( key ) &&
! Object . prototype . hasOwnProperty . call ( positionalMap , key ) &&
! Object . prototype . hasOwnProperty . call ( yargs . getInternalMethods ( ) . getParseContext ( ) , key ) &&
! self . isValidAndSomeAliasIsNotNew ( key , aliases ) ) {
unknown . push ( key ) ;
}
} ) ;
if ( checkPositionals &&
( currentContext . commands . length > 0 ||
commandKeys . length > 0 ||
isDefaultCommand ) ) {
argv . _ . slice ( currentContext . commands . length ) . forEach ( key => {
if ( ! commandKeys . includes ( '' + key ) ) {
unknown . push ( '' + key ) ;
}
} ) ;
}
if ( checkPositionals ) {
const demandedCommands = yargs . getDemandedCommands ( ) ;
const maxNonOptDemanded = ( ( _a = demandedCommands . _ ) === null || _a === void 0 ? void 0 : _a . max ) || 0 ;
const expected = currentContext . commands . length + maxNonOptDemanded ;
if ( expected < argv . _ . length ) {
argv . _ . slice ( expected ) . forEach ( key => {
key = String ( key ) ;
if ( ! currentContext . commands . includes ( key ) &&
! unknown . includes ( key ) ) {
unknown . push ( key ) ;
}
} ) ;
}
}
if ( unknown . length ) {
2023-04-21 16:12:50 +00:00
usage . fail ( _ _n ( 'Unknown argument: %s' , 'Unknown arguments: %s' , unknown . length , unknown . map ( s => ( s . trim ( ) ? s : ` " ${ s } " ` ) ) . join ( ', ' ) ) ) ;
2023-04-21 14:57:54 +00:00
}
} ;
self . unknownCommands = function unknownCommands ( argv ) {
const commandKeys = yargs
. getInternalMethods ( )
. getCommandInstance ( )
. getCommands ( ) ;
const unknown = [ ] ;
const currentContext = yargs . getInternalMethods ( ) . getContext ( ) ;
if ( currentContext . commands . length > 0 || commandKeys . length > 0 ) {
argv . _ . slice ( currentContext . commands . length ) . forEach ( key => {
if ( ! commandKeys . includes ( '' + key ) ) {
unknown . push ( '' + key ) ;
}
} ) ;
}
if ( unknown . length > 0 ) {
usage . fail ( _ _n ( 'Unknown command: %s' , 'Unknown commands: %s' , unknown . length , unknown . join ( ', ' ) ) ) ;
return true ;
}
else {
return false ;
}
} ;
self . isValidAndSomeAliasIsNotNew = function isValidAndSomeAliasIsNotNew ( key , aliases ) {
if ( ! Object . prototype . hasOwnProperty . call ( aliases , key ) ) {
return false ;
}
const newAliases = yargs . parsed . newAliases ;
return [ key , ... aliases [ key ] ] . some ( a => ! Object . prototype . hasOwnProperty . call ( newAliases , a ) || ! newAliases [ key ] ) ;
} ;
self . limitedChoices = function limitedChoices ( argv ) {
const options = yargs . getOptions ( ) ;
const invalid = { } ;
if ( ! Object . keys ( options . choices ) . length )
return ;
Object . keys ( argv ) . forEach ( key => {
if ( specialKeys . indexOf ( key ) === - 1 &&
Object . prototype . hasOwnProperty . call ( options . choices , key ) ) {
[ ] . concat ( argv [ key ] ) . forEach ( value => {
if ( options . choices [ key ] . indexOf ( value ) === - 1 &&
value !== undefined ) {
invalid [ key ] = ( invalid [ key ] || [ ] ) . concat ( value ) ;
}
} ) ;
}
} ) ;
const invalidKeys = Object . keys ( invalid ) ;
if ( ! invalidKeys . length )
return ;
let msg = _ _ ( 'Invalid values:' ) ;
invalidKeys . forEach ( key => {
msg += ` \n ${ _ _ ( 'Argument: %s, Given: %s, Choices: %s' , key , usage . stringifiedValues ( invalid [ key ] ) , usage . stringifiedValues ( options . choices [ key ] ) ) } ` ;
} ) ;
usage . fail ( msg ) ;
} ;
let implied = { } ;
self . implies = function implies ( key , value ) {
argsert ( '<string|object> [array|number|string]' , [ key , value ] , arguments . length ) ;
if ( typeof key === 'object' ) {
Object . keys ( key ) . forEach ( k => {
self . implies ( k , key [ k ] ) ;
} ) ;
}
else {
yargs . global ( key ) ;
if ( ! implied [ key ] ) {
implied [ key ] = [ ] ;
}
if ( Array . isArray ( value ) ) {
value . forEach ( i => self . implies ( key , i ) ) ;
}
else {
assertNotStrictEqual ( value , undefined , shim ) ;
implied [ key ] . push ( value ) ;
}
}
} ;
self . getImplied = function getImplied ( ) {
return implied ;
} ;
function keyExists ( argv , val ) {
const num = Number ( val ) ;
val = isNaN ( num ) ? val : num ;
if ( typeof val === 'number' ) {
val = argv . _ . length >= val ;
}
else if ( val . match ( /^--no-.+/ ) ) {
val = val . match ( /^--no-(.+)/ ) [ 1 ] ;
val = ! Object . prototype . hasOwnProperty . call ( argv , val ) ;
}
else {
val = Object . prototype . hasOwnProperty . call ( argv , val ) ;
}
return val ;
}
self . implications = function implications ( argv ) {
const implyFail = [ ] ;
Object . keys ( implied ) . forEach ( key => {
const origKey = key ;
( implied [ key ] || [ ] ) . forEach ( value => {
let key = origKey ;
const origValue = value ;
key = keyExists ( argv , key ) ;
value = keyExists ( argv , value ) ;
if ( key && ! value ) {
implyFail . push ( ` ${ origKey } -> ${ origValue } ` ) ;
}
} ) ;
} ) ;
if ( implyFail . length ) {
let msg = ` ${ _ _ ( 'Implications failed:' ) } \n ` ;
implyFail . forEach ( value => {
msg += value ;
} ) ;
usage . fail ( msg ) ;
}
} ;
let conflicting = { } ;
self . conflicts = function conflicts ( key , value ) {
argsert ( '<string|object> [array|string]' , [ key , value ] , arguments . length ) ;
if ( typeof key === 'object' ) {
Object . keys ( key ) . forEach ( k => {
self . conflicts ( k , key [ k ] ) ;
} ) ;
}
else {
yargs . global ( key ) ;
if ( ! conflicting [ key ] ) {
conflicting [ key ] = [ ] ;
}
if ( Array . isArray ( value ) ) {
value . forEach ( i => self . conflicts ( key , i ) ) ;
}
else {
conflicting [ key ] . push ( value ) ;
}
}
} ;
self . getConflicting = ( ) => conflicting ;
self . conflicting = function conflictingFn ( argv ) {
Object . keys ( argv ) . forEach ( key => {
if ( conflicting [ key ] ) {
conflicting [ key ] . forEach ( value => {
if ( value && argv [ key ] !== undefined && argv [ value ] !== undefined ) {
usage . fail ( _ _ ( 'Arguments %s and %s are mutually exclusive' , key , value ) ) ;
}
} ) ;
}
} ) ;
if ( yargs . getInternalMethods ( ) . getParserConfiguration ( ) [ 'strip-dashed' ] ) {
Object . keys ( conflicting ) . forEach ( key => {
conflicting [ key ] . forEach ( value => {
if ( value &&
argv [ shim . Parser . camelCase ( key ) ] !== undefined &&
argv [ shim . Parser . camelCase ( value ) ] !== undefined ) {
usage . fail ( _ _ ( 'Arguments %s and %s are mutually exclusive' , key , value ) ) ;
}
} ) ;
} ) ;
}
} ;
self . recommendCommands = function recommendCommands ( cmd , potentialCommands ) {
const threshold = 3 ;
potentialCommands = potentialCommands . sort ( ( a , b ) => b . length - a . length ) ;
let recommended = null ;
let bestDistance = Infinity ;
for ( let i = 0 , candidate ; ( candidate = potentialCommands [ i ] ) !== undefined ; i ++ ) {
const d = distance ( cmd , candidate ) ;
if ( d <= threshold && d < bestDistance ) {
bestDistance = d ;
recommended = candidate ;
}
}
if ( recommended )
usage . fail ( _ _ ( 'Did you mean %s?' , recommended ) ) ;
} ;
self . reset = function reset ( localLookup ) {
implied = objFilter ( implied , k => ! localLookup [ k ] ) ;
conflicting = objFilter ( conflicting , k => ! localLookup [ k ] ) ;
return self ;
} ;
const frozens = [ ] ;
self . freeze = function freeze ( ) {
frozens . push ( {
implied ,
conflicting ,
} ) ;
} ;
self . unfreeze = function unfreeze ( ) {
const frozen = frozens . pop ( ) ;
assertNotStrictEqual ( frozen , undefined , shim ) ;
( { implied , conflicting } = frozen ) ;
} ;
return self ;
}