mocha 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +1015 -0
  2. package/LICENSE +22 -0
  3. package/README.md +70 -0
  4. package/assets/growl/error.png +0 -0
  5. package/assets/growl/ok.png +0 -0
  6. package/bin/_mocha +10 -0
  7. package/bin/mocha +142 -0
  8. package/browser-entry.js +216 -0
  9. package/index.js +3 -0
  10. package/lib/browser/growl.js +169 -0
  11. package/lib/browser/highlight-tags.js +39 -0
  12. package/lib/browser/parse-query.js +24 -0
  13. package/lib/browser/progress.js +123 -0
  14. package/lib/browser/template.html +20 -0
  15. package/lib/cli/cli.js +89 -0
  16. package/lib/cli/collect-files.js +92 -0
  17. package/lib/cli/commands.js +13 -0
  18. package/lib/cli/config.js +105 -0
  19. package/lib/cli/index.js +3 -0
  20. package/lib/cli/init.js +36 -0
  21. package/lib/cli/lookup-files.js +145 -0
  22. package/lib/cli/node-flags.js +85 -0
  23. package/lib/cli/one-and-dones.js +69 -0
  24. package/lib/cli/options.js +261 -0
  25. package/lib/cli/run-helpers.js +243 -0
  26. package/lib/cli/run-option-metadata.js +117 -0
  27. package/lib/cli/run.js +379 -0
  28. package/lib/cli/watch-run.js +380 -0
  29. package/lib/context.js +86 -0
  30. package/lib/errors.js +563 -0
  31. package/lib/hook.js +89 -0
  32. package/lib/interfaces/bdd.js +111 -0
  33. package/lib/interfaces/common.js +193 -0
  34. package/lib/interfaces/exports.js +60 -0
  35. package/lib/interfaces/index.js +6 -0
  36. package/lib/interfaces/qunit.js +98 -0
  37. package/lib/interfaces/tdd.js +106 -0
  38. package/lib/mocha.js +1374 -0
  39. package/lib/mocharc.json +10 -0
  40. package/lib/nodejs/buffered-worker-pool.js +172 -0
  41. package/lib/nodejs/esm-utils.js +109 -0
  42. package/lib/nodejs/file-unloader.js +15 -0
  43. package/lib/nodejs/growl.js +137 -0
  44. package/lib/nodejs/parallel-buffered-runner.js +433 -0
  45. package/lib/nodejs/reporters/parallel-buffered.js +165 -0
  46. package/lib/nodejs/serializer.js +412 -0
  47. package/lib/nodejs/worker.js +151 -0
  48. package/lib/pending.js +16 -0
  49. package/lib/plugin-loader.js +286 -0
  50. package/lib/reporters/base.js +537 -0
  51. package/lib/reporters/doc.js +95 -0
  52. package/lib/reporters/dot.js +81 -0
  53. package/lib/reporters/html.js +390 -0
  54. package/lib/reporters/index.js +19 -0
  55. package/lib/reporters/json-stream.js +92 -0
  56. package/lib/reporters/json.js +162 -0
  57. package/lib/reporters/landing.js +116 -0
  58. package/lib/reporters/list.js +78 -0
  59. package/lib/reporters/markdown.js +112 -0
  60. package/lib/reporters/min.js +52 -0
  61. package/lib/reporters/nyan.js +276 -0
  62. package/lib/reporters/progress.js +104 -0
  63. package/lib/reporters/spec.js +99 -0
  64. package/lib/reporters/tap.js +293 -0
  65. package/lib/reporters/xunit.js +217 -0
  66. package/lib/runnable.js +476 -0
  67. package/lib/runner.js +1269 -0
  68. package/lib/stats-collector.js +83 -0
  69. package/lib/suite.js +695 -0
  70. package/lib/test.js +113 -0
  71. package/lib/utils.js +641 -0
  72. package/mocha-es2018.js +19816 -0
  73. package/mocha.css +325 -0
  74. package/mocha.js +30844 -0
  75. package/mocha.js.map +1 -0
  76. package/package.json +200 -0
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Parse the given `qs`.
5
+ *
6
+ * @private
7
+ * @param {string} qs
8
+ * @return {Object<string, string>}
9
+ */
10
+ module.exports = function parseQuery(qs) {
11
+ return qs
12
+ .replace('?', '')
13
+ .split('&')
14
+ .reduce(function(obj, pair) {
15
+ var i = pair.indexOf('=');
16
+ var key = pair.slice(0, i);
17
+ var val = pair.slice(++i);
18
+
19
+ // Due to how the URLSearchParams API treats spaces
20
+ obj[key] = decodeURIComponent(val.replace(/\+/g, '%20'));
21
+
22
+ return obj;
23
+ }, {});
24
+ };
@@ -0,0 +1,123 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ @module browser/Progress
5
+ */
6
+
7
+ /**
8
+ * Expose `Progress`.
9
+ */
10
+
11
+ module.exports = Progress;
12
+
13
+ /**
14
+ * Initialize a new `Progress` indicator.
15
+ */
16
+ function Progress() {
17
+ this.percent = 0;
18
+ this.size(0);
19
+ this.fontSize(11);
20
+ this.font('helvetica, arial, sans-serif');
21
+ }
22
+
23
+ /**
24
+ * Set progress size to `size`.
25
+ *
26
+ * @public
27
+ * @param {number} size
28
+ * @return {Progress} Progress instance.
29
+ */
30
+ Progress.prototype.size = function(size) {
31
+ this._size = size;
32
+ return this;
33
+ };
34
+
35
+ /**
36
+ * Set text to `text`.
37
+ *
38
+ * @public
39
+ * @param {string} text
40
+ * @return {Progress} Progress instance.
41
+ */
42
+ Progress.prototype.text = function(text) {
43
+ this._text = text;
44
+ return this;
45
+ };
46
+
47
+ /**
48
+ * Set font size to `size`.
49
+ *
50
+ * @public
51
+ * @param {number} size
52
+ * @return {Progress} Progress instance.
53
+ */
54
+ Progress.prototype.fontSize = function(size) {
55
+ this._fontSize = size;
56
+ return this;
57
+ };
58
+
59
+ /**
60
+ * Set font to `family`.
61
+ *
62
+ * @param {string} family
63
+ * @return {Progress} Progress instance.
64
+ */
65
+ Progress.prototype.font = function(family) {
66
+ this._font = family;
67
+ return this;
68
+ };
69
+
70
+ /**
71
+ * Update percentage to `n`.
72
+ *
73
+ * @param {number} n
74
+ * @return {Progress} Progress instance.
75
+ */
76
+ Progress.prototype.update = function(n) {
77
+ this.percent = n;
78
+ return this;
79
+ };
80
+
81
+ /**
82
+ * Draw on `ctx`.
83
+ *
84
+ * @param {CanvasRenderingContext2d} ctx
85
+ * @return {Progress} Progress instance.
86
+ */
87
+ Progress.prototype.draw = function(ctx) {
88
+ try {
89
+ var percent = Math.min(this.percent, 100);
90
+ var size = this._size;
91
+ var half = size / 2;
92
+ var x = half;
93
+ var y = half;
94
+ var rad = half - 1;
95
+ var fontSize = this._fontSize;
96
+
97
+ ctx.font = fontSize + 'px ' + this._font;
98
+
99
+ var angle = Math.PI * 2 * (percent / 100);
100
+ ctx.clearRect(0, 0, size, size);
101
+
102
+ // outer circle
103
+ ctx.strokeStyle = '#9f9f9f';
104
+ ctx.beginPath();
105
+ ctx.arc(x, y, rad, 0, angle, false);
106
+ ctx.stroke();
107
+
108
+ // inner circle
109
+ ctx.strokeStyle = '#eee';
110
+ ctx.beginPath();
111
+ ctx.arc(x, y, rad - 1, 0, angle, true);
112
+ ctx.stroke();
113
+
114
+ // text
115
+ var text = this._text || (percent | 0) + '%';
116
+ var w = ctx.measureText(text).width;
117
+
118
+ ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1);
119
+ } catch (ignore) {
120
+ // don't fail if we can't render progress
121
+ }
122
+ return this;
123
+ };
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Mocha</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <link rel="stylesheet" href="mocha.css" />
8
+ </head>
9
+ <body>
10
+ <div id="mocha"></div>
11
+ <script src="mocha.js"></script>
12
+ <script>
13
+ mocha.setup('bdd');
14
+ </script>
15
+ <script src="tests.spec.js"></script>
16
+ <script>
17
+ mocha.run();
18
+ </script>
19
+ </body>
20
+ </html>
package/lib/cli/cli.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ /**
6
+ * Contains CLI entry point and public API for programmatic usage in Node.js.
7
+ * - Option parsing is handled by {@link https://npm.im/yargs yargs}.
8
+ * - If executed via `node`, this module will run {@linkcode module:lib/cli.main main()}.
9
+ * @public
10
+ * @module lib/cli
11
+ */
12
+
13
+ const debug = require('debug')('mocha:cli:cli');
14
+ const symbols = require('log-symbols');
15
+ const yargs = require('yargs/yargs');
16
+ const path = require('path');
17
+ const {
18
+ loadRc,
19
+ loadPkgRc,
20
+ loadOptions,
21
+ YARGS_PARSER_CONFIG
22
+ } = require('./options');
23
+ const lookupFiles = require('./lookup-files');
24
+ const commands = require('./commands');
25
+ const ansi = require('ansi-colors');
26
+ const {repository, homepage, version, gitter} = require('../../package.json');
27
+ const {cwd} = require('../utils');
28
+
29
+ /**
30
+ * - Accepts an `Array` of arguments
31
+ * - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules
32
+ * - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity`
33
+ * @public
34
+ * @summary Mocha's main command-line entry-point.
35
+ * @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)`
36
+ * @param {object} [mochaArgs] - Object of already parsed Mocha arguments (by bin/mocha)
37
+ */
38
+ exports.main = (argv = process.argv.slice(2), mochaArgs) => {
39
+ debug('entered main with raw args', argv);
40
+ // ensure we can require() from current working directory
41
+ if (typeof module.paths !== 'undefined') {
42
+ module.paths.push(cwd(), path.resolve('node_modules'));
43
+ }
44
+
45
+ Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
46
+
47
+ var args = mochaArgs || loadOptions(argv);
48
+
49
+ yargs()
50
+ .scriptName('mocha')
51
+ .command(commands.run)
52
+ .command(commands.init)
53
+ .updateStrings({
54
+ 'Positionals:': 'Positional Arguments',
55
+ 'Options:': 'Other Options',
56
+ 'Commands:': 'Commands'
57
+ })
58
+ .fail((msg, err, yargs) => {
59
+ debug('caught error sometime before command handler: %O', err);
60
+ yargs.showHelp();
61
+ console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`);
62
+ process.exitCode = 1;
63
+ })
64
+ .help('help', 'Show usage information & exit')
65
+ .alias('help', 'h')
66
+ .version('version', 'Show version number & exit', version)
67
+ .alias('version', 'V')
68
+ .wrap(process.stdout.columns ? Math.min(process.stdout.columns, 80) : 80)
69
+ .epilog(
70
+ `Mocha Resources
71
+ Chat: ${ansi.magenta(gitter)}
72
+ GitHub: ${ansi.blue(repository.url)}
73
+ Docs: ${ansi.yellow(homepage)}
74
+ `
75
+ )
76
+ .parserConfiguration(YARGS_PARSER_CONFIG)
77
+ .config(args)
78
+ .parse(args._);
79
+ };
80
+
81
+ exports.lookupFiles = lookupFiles;
82
+ exports.loadOptions = loadOptions;
83
+ exports.loadPkgRc = loadPkgRc;
84
+ exports.loadRc = loadRc;
85
+
86
+ // allow direct execution
87
+ if (require.main === module) {
88
+ exports.main();
89
+ }
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const ansi = require('ansi-colors');
5
+ const debug = require('debug')('mocha:cli:run:helpers');
6
+ const minimatch = require('minimatch');
7
+ const {NO_FILES_MATCH_PATTERN} = require('../errors').constants;
8
+ const lookupFiles = require('./lookup-files');
9
+ const {castArray} = require('../utils');
10
+
11
+ /**
12
+ * Exports a function that collects test files from CLI parameters.
13
+ * @see module:lib/cli/run-helpers
14
+ * @see module:lib/cli/watch-run
15
+ * @module
16
+ * @private
17
+ */
18
+
19
+ /**
20
+ * Smash together an array of test files in the correct order
21
+ * @param {FileCollectionOptions} [opts] - Options
22
+ * @returns {string[]} List of files to test
23
+ * @private
24
+ */
25
+ module.exports = ({
26
+ ignore,
27
+ extension,
28
+ file: fileArgs,
29
+ recursive,
30
+ sort,
31
+ spec
32
+ } = {}) => {
33
+ const unmatched = [];
34
+ const specFiles = spec.reduce((specFiles, arg) => {
35
+ try {
36
+ const moreSpecFiles = castArray(lookupFiles(arg, extension, recursive))
37
+ .filter(filename =>
38
+ ignore.every(pattern => !minimatch(filename, pattern))
39
+ )
40
+ .map(filename => path.resolve(filename));
41
+ return [...specFiles, ...moreSpecFiles];
42
+ } catch (err) {
43
+ if (err.code === NO_FILES_MATCH_PATTERN) {
44
+ unmatched.push({message: err.message, pattern: err.pattern});
45
+ return specFiles;
46
+ }
47
+
48
+ throw err;
49
+ }
50
+ }, []);
51
+
52
+ // ensure we don't sort the stuff from fileArgs; order is important!
53
+ if (sort) {
54
+ specFiles.sort();
55
+ }
56
+
57
+ // add files given through --file to be ran first
58
+ const files = [
59
+ ...fileArgs.map(filepath => path.resolve(filepath)),
60
+ ...specFiles
61
+ ];
62
+ debug('test files (in order): ', files);
63
+
64
+ if (!files.length) {
65
+ // give full message details when only 1 file is missing
66
+ const noneFoundMsg =
67
+ unmatched.length === 1
68
+ ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw
69
+ : 'Error: No test files found';
70
+ console.error(ansi.red(noneFoundMsg));
71
+ process.exit(1);
72
+ } else {
73
+ // print messages as a warning
74
+ unmatched.forEach(warning => {
75
+ console.warn(ansi.yellow(`Warning: ${warning.message}`));
76
+ });
77
+ }
78
+
79
+ return files;
80
+ };
81
+
82
+ /**
83
+ * An object to configure how Mocha gathers test files
84
+ * @private
85
+ * @typedef {Object} FileCollectionOptions
86
+ * @property {string[]} extension - File extensions to use
87
+ * @property {string[]} spec - Files, dirs, globs to run
88
+ * @property {string[]} ignore - Files, dirs, globs to ignore
89
+ * @property {string[]} file - List of additional files to include
90
+ * @property {boolean} recursive - Find files recursively
91
+ * @property {boolean} sort - Sort test files
92
+ */
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Exports Yargs commands
5
+ * @see https://git.io/fpJ0G
6
+ * @private
7
+ * @module
8
+ */
9
+
10
+ exports.init = require('./init');
11
+
12
+ // default command
13
+ exports.run = require('./run');
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Responsible for loading / finding Mocha's "rc" files.
5
+ *
6
+ * @private
7
+ * @module
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const debug = require('debug')('mocha:cli:config');
13
+ const findUp = require('find-up');
14
+ const {createUnparsableFileError} = require('../errors');
15
+ const utils = require('../utils');
16
+
17
+ /**
18
+ * These are the valid config files, in order of precedence;
19
+ * e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest
20
+ * will be ignored.
21
+ * The user should still be able to explicitly specify a file.
22
+ * @private
23
+ */
24
+ exports.CONFIG_FILES = [
25
+ '.mocharc.cjs',
26
+ '.mocharc.js',
27
+ '.mocharc.yaml',
28
+ '.mocharc.yml',
29
+ '.mocharc.jsonc',
30
+ '.mocharc.json'
31
+ ];
32
+
33
+ const isModuleNotFoundError = err =>
34
+ err.code === 'MODULE_NOT_FOUND' ||
35
+ err.message.indexOf('Cannot find module') !== -1;
36
+
37
+ /**
38
+ * Parsers for various config filetypes. Each accepts a filepath and
39
+ * returns an object (but could throw)
40
+ */
41
+ const parsers = (exports.parsers = {
42
+ yaml: filepath => require('js-yaml').load(fs.readFileSync(filepath, 'utf8')),
43
+ js: filepath => {
44
+ const cwdFilepath = path.resolve(filepath);
45
+ try {
46
+ debug('parsers: load using cwd-relative path: "%s"', cwdFilepath);
47
+ return require(cwdFilepath);
48
+ } catch (err) {
49
+ if (isModuleNotFoundError(err)) {
50
+ debug('parsers: retry load as module-relative path: "%s"', filepath);
51
+ return require(filepath);
52
+ } else {
53
+ throw err; // rethrow
54
+ }
55
+ }
56
+ },
57
+ json: filepath =>
58
+ JSON.parse(
59
+ require('strip-json-comments')(fs.readFileSync(filepath, 'utf8'))
60
+ )
61
+ });
62
+
63
+ /**
64
+ * Loads and parses, based on file extension, a config file.
65
+ * "JSON" files may have comments.
66
+ *
67
+ * @private
68
+ * @param {string} filepath - Config file path to load
69
+ * @returns {Object} Parsed config object
70
+ */
71
+ exports.loadConfig = filepath => {
72
+ let config = {};
73
+ debug('loadConfig: trying to parse config at %s', filepath);
74
+
75
+ const ext = path.extname(filepath);
76
+ try {
77
+ if (ext === '.yml' || ext === '.yaml') {
78
+ config = parsers.yaml(filepath);
79
+ } else if (ext === '.js' || ext === '.cjs') {
80
+ config = parsers.js(filepath);
81
+ } else {
82
+ config = parsers.json(filepath);
83
+ }
84
+ } catch (err) {
85
+ throw createUnparsableFileError(
86
+ `Unable to read/parse ${filepath}: ${err}`,
87
+ filepath
88
+ );
89
+ }
90
+ return config;
91
+ };
92
+
93
+ /**
94
+ * Find ("find up") config file starting at `cwd`
95
+ *
96
+ * @param {string} [cwd] - Current working directory
97
+ * @returns {string|null} Filepath to config, if found
98
+ */
99
+ exports.findConfig = (cwd = utils.cwd()) => {
100
+ const filepath = findUp.sync(exports.CONFIG_FILES, {cwd});
101
+ if (filepath) {
102
+ debug('findConfig: found config file %s', filepath);
103
+ }
104
+ return filepath;
105
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('./cli');
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Command module for "init" command
5
+ *
6
+ * @private
7
+ * @module
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ exports.command = 'init <path>';
14
+
15
+ exports.description = 'create a client-side Mocha setup at <path>';
16
+
17
+ exports.builder = yargs =>
18
+ yargs.positional('path', {
19
+ type: 'string',
20
+ normalize: true
21
+ });
22
+
23
+ exports.handler = argv => {
24
+ const destdir = argv.path;
25
+ const srcdir = path.join(__dirname, '..', '..');
26
+ fs.mkdirSync(destdir, {recursive: true});
27
+ const css = fs.readFileSync(path.join(srcdir, 'mocha.css'));
28
+ const js = fs.readFileSync(path.join(srcdir, 'mocha.js'));
29
+ const tmpl = fs.readFileSync(
30
+ path.join(srcdir, 'lib', 'browser', 'template.html')
31
+ );
32
+ fs.writeFileSync(path.join(destdir, 'mocha.css'), css);
33
+ fs.writeFileSync(path.join(destdir, 'mocha.js'), js);
34
+ fs.writeFileSync(path.join(destdir, 'tests.spec.js'), '');
35
+ fs.writeFileSync(path.join(destdir, 'index.html'), tmpl);
36
+ };
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+ /**
3
+ * Contains `lookupFiles`, which takes some globs/dirs/options and returns a list of files.
4
+ * @module
5
+ * @private
6
+ */
7
+
8
+ var fs = require('fs');
9
+ var path = require('path');
10
+ var glob = require('glob');
11
+ var errors = require('../errors');
12
+ var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
13
+ var createMissingArgumentError = errors.createMissingArgumentError;
14
+ const debug = require('debug')('mocha:cli:lookup-files');
15
+
16
+ /**
17
+ * Determines if pathname would be a "hidden" file (or directory) on UN*X.
18
+ *
19
+ * @description
20
+ * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
21
+ * typical usage. Dotfiles, plain-text configuration files, are prime examples.
22
+ *
23
+ * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
24
+ *
25
+ * @private
26
+ * @param {string} pathname - Pathname to check for match.
27
+ * @return {boolean} whether pathname would be considered a hidden file.
28
+ * @example
29
+ * isHiddenOnUnix('.profile'); // => true
30
+ */
31
+ const isHiddenOnUnix = pathname => path.basename(pathname).startsWith('.');
32
+
33
+ /**
34
+ * Determines if pathname has a matching file extension.
35
+ *
36
+ * Supports multi-part extensions.
37
+ *
38
+ * @private
39
+ * @param {string} pathname - Pathname to check for match.
40
+ * @param {string[]} exts - List of file extensions, w/-or-w/o leading period
41
+ * @return {boolean} `true` if file extension matches.
42
+ * @example
43
+ * hasMatchingExtname('foo.html', ['js', 'css']); // false
44
+ * hasMatchingExtname('foo.js', ['.js']); // true
45
+ * hasMatchingExtname('foo.js', ['js']); // ture
46
+ */
47
+ const hasMatchingExtname = (pathname, exts = []) =>
48
+ exts
49
+ .map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
50
+ .some(ext => pathname.endsWith(ext));
51
+
52
+ /**
53
+ * Lookup file names at the given `path`.
54
+ *
55
+ * @description
56
+ * Filenames are returned in _traversal_ order by the OS/filesystem.
57
+ * **Make no assumption that the names will be sorted in any fashion.**
58
+ *
59
+ * @public
60
+ * @alias module:lib/cli.lookupFiles
61
+ * @param {string} filepath - Base path to start searching from.
62
+ * @param {string[]} [extensions=[]] - File extensions to look for.
63
+ * @param {boolean} [recursive=false] - Whether to recurse into subdirectories.
64
+ * @return {string[]} An array of paths.
65
+ * @throws {Error} if no files match pattern.
66
+ * @throws {TypeError} if `filepath` is directory and `extensions` not provided.
67
+ */
68
+ module.exports = function lookupFiles(
69
+ filepath,
70
+ extensions = [],
71
+ recursive = false
72
+ ) {
73
+ const files = [];
74
+ let stat;
75
+
76
+ if (!fs.existsSync(filepath)) {
77
+ let pattern;
78
+ if (glob.hasMagic(filepath)) {
79
+ // Handle glob as is without extensions
80
+ pattern = filepath;
81
+ } else {
82
+ // glob pattern e.g. 'filepath+(.js|.ts)'
83
+ const strExtensions = extensions
84
+ .map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
85
+ .join('|');
86
+ pattern = `${filepath}+(${strExtensions})`;
87
+ debug('looking for files using glob pattern: %s', pattern);
88
+ }
89
+ files.push(...glob.sync(pattern, {nodir: true}));
90
+ if (!files.length) {
91
+ throw createNoFilesMatchPatternError(
92
+ `Cannot find any files matching pattern "${filepath}"`,
93
+ filepath
94
+ );
95
+ }
96
+ return files;
97
+ }
98
+
99
+ // Handle file
100
+ try {
101
+ stat = fs.statSync(filepath);
102
+ if (stat.isFile()) {
103
+ return filepath;
104
+ }
105
+ } catch (err) {
106
+ // ignore error
107
+ return;
108
+ }
109
+
110
+ // Handle directory
111
+ fs.readdirSync(filepath).forEach(dirent => {
112
+ const pathname = path.join(filepath, dirent);
113
+ let stat;
114
+
115
+ try {
116
+ stat = fs.statSync(pathname);
117
+ if (stat.isDirectory()) {
118
+ if (recursive) {
119
+ files.push(...lookupFiles(pathname, extensions, recursive));
120
+ }
121
+ return;
122
+ }
123
+ } catch (ignored) {
124
+ return;
125
+ }
126
+ if (!extensions.length) {
127
+ throw createMissingArgumentError(
128
+ `Argument '${extensions}' required when argument '${filepath}' is a directory`,
129
+ 'extensions',
130
+ 'array'
131
+ );
132
+ }
133
+
134
+ if (
135
+ !stat.isFile() ||
136
+ !hasMatchingExtname(pathname, extensions) ||
137
+ isHiddenOnUnix(pathname)
138
+ ) {
139
+ return;
140
+ }
141
+ files.push(pathname);
142
+ });
143
+
144
+ return files;
145
+ };