UNPKG

2.58 kBJavaScriptView Raw
1const fs = require('fs');
2const globrex = require('globrex');
3const { promisify } = require('util');
4const globalyzer = require('globalyzer');
5const { join, resolve, relative } = require('path');
6const isHidden = /(^|[\\\/])\.[^\\\/\.]/g;
7const readdir = promisify(fs.readdir);
8const stat = promisify(fs.stat);
9let CACHE = {};
10
11async function walk(output, prefix, lexer, opts, dirname='', level=0) {
12 const rgx = lexer.segments[level];
13 const dir = resolve(opts.cwd, prefix, dirname);
14 const files = await readdir(dir);
15 const { dot, filesOnly } = opts;
16
17 let i=0, len=files.length, file;
18 let fullpath, relpath, stats, isMatch;
19
20 for (; i < len; i++) {
21 fullpath = join(dir, file=files[i]);
22 relpath = dirname ? join(dirname, file) : file;
23 if (!dot && isHidden.test(relpath)) continue;
24 isMatch = lexer.regex.test(relpath);
25
26 if ((stats=CACHE[relpath]) === void 0) {
27 CACHE[relpath] = stats = fs.lstatSync(fullpath);
28 }
29
30 if (!stats.isDirectory()) {
31 isMatch && output.push(relative(opts.cwd, fullpath));
32 continue;
33 }
34
35 if (rgx && !rgx.test(file)) continue;
36 !filesOnly && isMatch && output.push(join(prefix, relpath));
37
38 await walk(output, prefix, lexer, opts, relpath, rgx && rgx.toString() !== lexer.globstar && level + 1);
39 }
40}
41
42/**
43 * Find files using bash-like globbing.
44 * All paths are normalized compared to node-glob.
45 * @param {String} str Glob string
46 * @param {String} [options.cwd='.'] Current working directory
47 * @param {Boolean} [options.dot=false] Include dotfile matches
48 * @param {Boolean} [options.absolute=false] Return absolute paths
49 * @param {Boolean} [options.filesOnly=false] Do not include folders if true
50 * @param {Boolean} [options.flush=false] Reset cache object
51 * @returns {Array} array containing matching files
52 */
53module.exports = async function (str, opts={}) {
54 if (!str) return [];
55
56 let glob = globalyzer(str);
57
58 opts.cwd = opts.cwd || '.';
59
60 if (!glob.isGlob) {
61 try {
62 let resolved = resolve(opts.cwd, str);
63 let dirent = await stat(resolved);
64 if (opts.filesOnly && !dirent.isFile()) return [];
65
66 return opts.absolute ? [resolved] : [str];
67 } catch (err) {
68 if (err.code != 'ENOENT') throw err;
69
70 return [];
71 }
72 }
73
74 if (opts.flush) CACHE = {};
75
76 let matches = [];
77 const { path } = globrex(glob.glob, { filepath:true, globstar:true, extended:true });
78
79 path.globstar = path.globstar.toString();
80 await walk(matches, glob.base, path, opts, '.', 0);
81
82 return opts.absolute ? matches.map(x => resolve(opts.cwd, x)) : matches;
83};