UNPKG

mocha

Version:

Classic, reliable, trusted test framework for Node.js and the browser

146 lines (119 loc) 4.18 kB
/** * 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(); }