'use strict'; /** * Responsible for loading / finding Mocha's "rc" files. * * @private * @module */ const fs = require('fs'); const path = require('path'); const debug = require('debug')('mocha:cli:config'); const findUp = require('find-up'); const {createUnparsableFileError} = require('../errors'); const utils = require('../utils'); /** * These are the valid config files, in order of precedence; * e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest * will be ignored. * The user should still be able to explicitly specify a file. * @private */ exports.CONFIG_FILES = [ '.mocharc.cjs', '.mocharc.js', '.mocharc.yaml', '.mocharc.yml', '.mocharc.jsonc', '.mocharc.json' ]; const isModuleNotFoundError = err => err.code === 'MODULE_NOT_FOUND' || err.message.indexOf('Cannot find module') !== -1; /** * Parsers for various config filetypes. Each accepts a filepath and * returns an object (but could throw) */ const parsers = (exports.parsers = { yaml: filepath => require('js-yaml').load(fs.readFileSync(filepath, 'utf8')), js: filepath => { const cwdFilepath = path.resolve(filepath); try { debug('parsers: load using cwd-relative path: "%s"', cwdFilepath); return require(cwdFilepath); } catch (err) { if (isModuleNotFoundError(err)) { debug('parsers: retry load as module-relative path: "%s"', filepath); return require(filepath); } else { throw err; // rethrow } } }, json: filepath => JSON.parse( require('strip-json-comments')(fs.readFileSync(filepath, 'utf8')) ) }); /** * Loads and parses, based on file extension, a config file. * "JSON" files may have comments. * * @private * @param {string} filepath - Config file path to load * @returns {Object} Parsed config object */ exports.loadConfig = filepath => { let config = {}; debug('loadConfig: trying to parse config at %s', filepath); const ext = path.extname(filepath); try { if (ext === '.yml' || ext === '.yaml') { config = parsers.yaml(filepath); } else if (ext === '.js' || ext === '.cjs') { config = parsers.js(filepath); } else { config = parsers.json(filepath); } } catch (err) { throw createUnparsableFileError( `Unable to read/parse ${filepath}: ${err}`, filepath ); } return config; }; /** * Find ("find up") config file starting at `cwd` * * @param {string} [cwd] - Current working directory * @returns {string|null} Filepath to config, if found */ exports.findConfig = (cwd = utils.cwd()) => { const filepath = findUp.sync(exports.CONFIG_FILES, {cwd}); if (filepath) { debug('findConfig: found config file %s', filepath); } return filepath; };