| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- 'use strict';
- /**
- * Contains `lookupFiles`, which takes some globs/dirs/options and returns a list of files.
- * @module
- * @private
- */
- var fs = require('node:fs');
- var path = require('node:path');
- var glob = require('glob');
- var errors = require('../errors');
- var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
- var createMissingArgumentError = errors.createMissingArgumentError;
- const debug = require('debug')('mocha:cli:lookup-files');
- /**
- * Determines if pathname would be a "hidden" file (or directory) on UN*X.
- *
- * @description
- * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
- * typical usage. Dotfiles, plain-text configuration files, are prime examples.
- *
- * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
- *
- * @private
- * @param {string} pathname - Pathname to check for match.
- * @return {boolean} whether pathname would be considered a hidden file.
- * @example
- * isHiddenOnUnix('.profile'); // => true
- */
- const isHiddenOnUnix = pathname => path.basename(pathname).startsWith('.');
- /**
- * Determines if pathname has a matching file extension.
- *
- * Supports multi-part extensions.
- *
- * @private
- * @param {string} pathname - Pathname to check for match.
- * @param {string[]} exts - List of file extensions, w/-or-w/o leading period
- * @return {boolean} `true` if file extension matches.
- * @example
- * hasMatchingExtname('foo.html', ['js', 'css']); // false
- * hasMatchingExtname('foo.js', ['.js']); // true
- * hasMatchingExtname('foo.js', ['js']); // ture
- */
- const hasMatchingExtname = (pathname, exts = []) =>
- exts
- .map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
- .some(ext => pathname.endsWith(ext));
- /**
- * Lookup file names at the given `path`.
- *
- * @description
- * Filenames are returned in _traversal_ order by the OS/filesystem.
- * **Make no assumption that the names will be sorted in any fashion.**
- *
- * @public
- * @alias module:lib/cli.lookupFiles
- * @param {string} filepath - Base path to start searching from.
- * @param {string[]} [extensions=[]] - File extensions to look for.
- * @param {boolean} [recursive=false] - Whether to recurse into subdirectories.
- * @return {string[]} An array of paths.
- * @throws {Error} if no files match pattern.
- * @throws {TypeError} if `filepath` is directory and `extensions` not provided.
- */
- module.exports = function lookupFiles(
- filepath,
- extensions = [],
- recursive = false
- ) {
- const files = [];
- let stat;
- if (!fs.existsSync(filepath)) {
- let pattern;
- if (glob.hasMagic(filepath, {windowsPathsNoEscape: true})) {
- // Handle glob as is without extensions
- pattern = filepath;
- } else {
- // glob pattern e.g. 'filepath+(.js|.ts)'
- const strExtensions = extensions
- .map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
- .join('|');
- pattern = `${filepath}+(${strExtensions})`;
- debug('looking for files using glob pattern: %s', pattern);
- }
- files.push(
- ...glob
- .sync(pattern, {
- nodir: true,
- windowsPathsNoEscape: true
- })
- // glob@8 and earlier sorted results in en; glob@9 depends on OS sorting.
- // This preserves the older glob behavior.
- // https://github.com/mochajs/mocha/pull/5250/files#r1840469747
- .sort((a, b) => a.localeCompare(b, 'en'))
- );
- if (!files.length) {
- throw createNoFilesMatchPatternError(
- `Cannot find any files matching pattern "${filepath}"`,
- filepath
- );
- }
- return files;
- }
- // Handle file
- try {
- stat = fs.statSync(filepath);
- if (stat.isFile()) {
- return filepath;
- }
- } catch (err) {
- // ignore error
- return;
- }
- // Handle directory
- fs.readdirSync(filepath).forEach(dirent => {
- const pathname = path.join(filepath, dirent);
- let stat;
- try {
- stat = fs.statSync(pathname);
- if (stat.isDirectory()) {
- if (recursive) {
- files.push(...lookupFiles(pathname, extensions, recursive));
- }
- return;
- }
- } catch (ignored) {
- return;
- }
- if (!extensions.length) {
- throw createMissingArgumentError(
- `Argument '${extensions}' required when argument '${filepath}' is a directory`,
- 'extensions',
- 'array'
- );
- }
- if (
- !stat.isFile() ||
- !hasMatchingExtname(pathname, extensions) ||
- isHiddenOnUnix(pathname)
- ) {
- return;
- }
- files.push(pathname);
- });
- return files;
- };
|