Skip to content

Commit

Permalink
Find flag for mocha cli after parsing yargs
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinika committed Dec 3, 2024
1 parent 6c33877 commit a8ea4dd
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 21 deletions.
73 changes: 52 additions & 21 deletions lib/cli/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,25 @@
const fs = require('fs');
const ansi = require('ansi-colors');
const yargsParser = require('yargs-parser');
const {types, aliases} = require('./run-option-metadata');
const {
types,
aliases,
isMochaFlag,
expectedTypeForFlag
} = require('./run-option-metadata');
const {ONE_AND_DONE_ARGS} = require('./one-and-dones');
const mocharc = require('../mocharc.json');
const {list} = require('./run-helpers');
const {loadConfig, findConfig} = require('./config');
const findUp = require('find-up');
const debug = require('debug')('mocha:cli:options');
const {isNodeFlag} = require('./node-flags');
const {createUnparsableFileError} = require('../errors');
const {
createUnparsableFileError,
createInvalidArgumentTypeError,
createUnsupportedError
} = require('../errors');
const {isNumeric} = require('../utils');

/**
* The `yargs-parser` namespace
Expand Down Expand Up @@ -108,23 +118,21 @@ const parse = (args = [], defaultValues = {}, ...configObjects) => {
// 3. to avoid explicitly defining the set of them, we tell yargs-parser they
// are ALL boolean flags.
// 4. we can then reapply the values after yargs-parser is done.
const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce(
(acc, arg) => {
if (typeof arg !== 'string') {
throw new Error(`Invalid option ${arg} passed to mocha cli`);
}
const pair = arg.split('=');
let flag = pair[0];
if (isNodeFlag(flag, false)) {
flag = flag.replace(/^--?/, '');
return arg.includes('=')
? acc.concat([[flag, pair[1]]])
: acc.concat([[flag, true]]);
}
return acc;
},
[]
);
const allArgs = Array.isArray(args) ? args : args.split(' ');
const nodeArgs = allArgs.reduce((acc, arg) => {
if (typeof arg !== 'string') {
throw new Error(`Invalid option ${arg} passed to mocha cli`);
}
const pair = arg.split('=');
let flag = pair[0];
if (isNodeFlag(flag, false)) {
flag = flag.replace(/^--?/, '');
return arg.includes('=')
? acc.concat([[flag, pair[1]]])
: acc.concat([[flag, true]]);
}
return acc;
}, []);

const result = yargsParser.detailed(args, {
configuration,
Expand All @@ -148,6 +156,30 @@ const parse = (args = [], defaultValues = {}, ...configObjects) => {
result.argv[key] = value;
});

const numericPositionalArgs = result.argv._.filter(arg => isNumeric(arg));
numericPositionalArgs.forEach(numericArg => {
const flag = allArgs
.map(arg => arg.replace(/^--?/, ''))
.find((arg, index) => {
return (
isMochaFlag(arg) &&
args[index + 1] === String(numericArg) &&
String(result.argv[arg]) !== String(numericArg)
);
});
if (flag) {
throw createInvalidArgumentTypeError(
`Flag ${flag} has invalid arg ${numericArg}`,
numericArg,
expectedTypeForFlag(flag)
);
} else {
throw createUnsupportedError(
`Invalid option ${numericArg} passed to mocha cli`
);
}
});

return result.argv;
};

Expand Down Expand Up @@ -195,8 +227,7 @@ const loadPkgRc = (args = {}) => {
filepath
);
} else {
debug('failed to read default package.json at %s; ignoring',
filepath);
debug('failed to read default package.json at %s; ignoring', filepath);
return result;
}
}
Expand Down
20 changes: 20 additions & 0 deletions lib/cli/run-option-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,23 @@ const ALL_MOCHA_FLAGS = Object.keys(TYPES).reduce((acc, key) => {
exports.isMochaFlag = flag => {
return ALL_MOCHA_FLAGS.has(flag.replace(/^--?/, ''));
};

/**
* Returns expected yarg option type for a given mocha flag.
* @param {string} flag - Flag to check (can be with out without leading "--"")
* @returns {string | undefined} - If flag is a valid mocha flag, the expected type of argument for this flag is returned, otherwise undefined is returned.
*/
exports.expectedTypeForFlag = flag => {
const normalizedName = flag?.replace(/^--?/, '');

// If flag is an alias, get it's full name.
const aliases = exports.aliases;
const fullFlagName =
Object.keys(aliases).find(flagName =>
aliases[flagName].includes(normalizedName)
) || normalizedName;

return Object.keys(TYPES).find(flagType =>
TYPES[flagType].includes(fullFlagName)
);
};
7 changes: 7 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -689,3 +689,10 @@ exports.breakCircularDeps = inputObj => {

return _breakCircularDeps(inputObj);
};

/**
* Checks if provided input can be parsed as a JavaScript Number.
*/
exports.isNumeric = input => {
return !isNaN(parseFloat(input));
};

0 comments on commit a8ea4dd

Please sign in to comment.