| /** | |
| * Contains CLI entry point and public API for programmatic usage in Node.js. | |
| * - Option parsing is handled by {@link module:lib/cli/parse-args}. | |
| * - If executed via `node`, this module will run {@linkcode module:lib/cli.main main()}. | |
| * @public | |
| * @module lib/cli | |
| */ | |
| import debugModule from "debug"; | |
| import { fileURLToPath } from "node:url"; | |
| import { loadOptions } from "./options.cjs"; | |
| import { run, init } from "./commands.js"; | |
| import pc from "picocolors"; | |
| import { logSymbols } from "../utils.cjs"; | |
| import packageJson from "../../package.json" with { type: "json" }; | |
| import { getRunOptionDefinitions } from "./run-option-metadata.cjs"; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const debug = debugModule("mocha:cli:cli"); | |
| const hasFlag = (args, longName, shortName) => | |
| args.includes(`--${longName}`) || args.includes(`-${shortName}`); | |
| const formatOption = (option) => { | |
| const aliases = option.aliases.map((alias) => | |
| alias.length === 1 ? `-${alias}` : `--${alias}`, | |
| ); | |
| return [`--${option.name}`].concat(aliases).join(", "); | |
| }; | |
| const printVersion = () => { | |
| console.log(packageJson.version); | |
| }; | |
| const printHelp = () => { | |
| const options = getRunOptionDefinitions() | |
| .filter((option) => option.description) | |
| .map( | |
| (option) => ` ${formatOption(option).padEnd(34)} ${option.description}`, | |
| ) | |
| .join("\n"); | |
| console.log(`Usage: mocha [spec..] | |
| mocha init <path> | |
| Commands: | |
| init <path> ${init.description} | |
| Options: | |
| ${options} | |
| --help, -h${"".padEnd(24)} Show usage information & exit | |
| --version, -V${"".padEnd(21)} Show version number & exit | |
| Mocha Resources | |
| Chat: ${packageJson.discord} | |
| GitHub: ${packageJson.repository.url} | |
| Docs: ${packageJson.homepage}`); | |
| }; | |
| const fail = (msg, err) => { | |
| debug("caught error before command handler: %O", err); | |
| printHelp(); | |
| console.error(`\n${logSymbols.error} ${pc.red("ERROR:")} ${msg}`); | |
| if (!msg && err) { | |
| // Log raw error and stack when an unexpected error is encountered, to | |
| // make debugging easier (instead of an inactionable "ERROR: null"). | |
| console.error(err); | |
| } | |
| process.exit(1); | |
| }; | |
| const failRunPreparation = (err) => { | |
| console.error(`\n${logSymbols.error} ${pc.red("ERROR:")}`, err); | |
| process.exit(1); | |
| }; | |
| const runCommand = async (args) => { | |
| args.spec = args.spec || (args._ && args._.length ? args._ : ["test"]); | |
| try { | |
| run.normalizeRunOptions(args); | |
| run.validateRunOptions(args); | |
| } catch (err) { | |
| fail(err && err.message, err); | |
| } | |
| try { | |
| await run.prepareRunOptions(args); | |
| } catch (err) { | |
| failRunPreparation(err); | |
| } | |
| return run.handler(args); | |
| }; | |
| /** | |
| * - Accepts an `Array` of arguments | |
| * - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules | |
| * - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity` | |
| * @public | |
| * @summary Mocha's main command-line entry-point. | |
| * @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)` | |
| * @param {object} [mochaArgs] - Object of already parsed Mocha arguments (by bin/mocha) | |
| */ | |
| export function main(argv = process.argv.slice(2), mochaArgs) { | |
| debug("entered main with raw args", argv); | |
| try { | |
| Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit? | |
| } catch (err) { | |
| debug("unable to set Error.stackTraceLimit = Infinity", err); | |
| } | |
| try { | |
| const args = mochaArgs ?? loadOptions(argv); | |
| if (hasFlag(argv, "help", "h") || args.help || args.h) { | |
| printHelp(); | |
| process.exit(0); | |
| } | |
| if (hasFlag(argv, "version", "V") || args.version || args.V) { | |
| printVersion(); | |
| process.exit(0); | |
| } | |
| const command = (args._ && args._[0]) || argv[0]; | |
| if (command === "init") { | |
| const commandArgs = | |
| args._ && args._[0] === "init" ? args._.slice(1) : argv.slice(1); | |
| return init.handler(init.parse(commandArgs)); | |
| } | |
| return runCommand(args); | |
| } catch (err) { | |
| fail(err && err.message, err); | |
| } | |
| } | |
| // allow direct execution | |
| if (__filename === process.argv[1]) { | |
| main(); | |
| } |