1import { Minimatch } from 'minimatch'; 2import { PathScurry, PathScurryDarwin, PathScurryPosix, PathScurryWin32, } from 'path-scurry'; 3import { fileURLToPath } from 'url'; 4import { Pattern } from './pattern.js'; 5import { GlobStream, GlobWalker } from './walker.js'; 6// if no process global, just call it linux. 7// so we default to case-sensitive, / separators 8const defaultPlatform = typeof process === 'object' && 9 process && 10 typeof process.platform === 'string' 11 ? process.platform 12 : 'linux'; 13/** 14 * An object that can perform glob pattern traversals. 15 */ 16export class Glob { 17 absolute; 18 cwd; 19 root; 20 dot; 21 dotRelative; 22 follow; 23 ignore; 24 magicalBraces; 25 mark; 26 matchBase; 27 maxDepth; 28 nobrace; 29 nocase; 30 nodir; 31 noext; 32 noglobstar; 33 pattern; 34 platform; 35 realpath; 36 scurry; 37 stat; 38 signal; 39 windowsPathsNoEscape; 40 withFileTypes; 41 /** 42 * The options provided to the constructor. 43 */ 44 opts; 45 /** 46 * An array of parsed immutable {@link Pattern} objects. 47 */ 48 patterns; 49 /** 50 * All options are stored as properties on the `Glob` object. 51 * 52 * See {@link GlobOptions} for full options descriptions. 53 * 54 * Note that a previous `Glob` object can be passed as the 55 * `GlobOptions` to another `Glob` instantiation to re-use settings 56 * and caches with a new pattern. 57 * 58 * Traversal functions can be called multiple times to run the walk 59 * again. 60 */ 61 constructor(pattern, opts) { 62 this.withFileTypes = !!opts.withFileTypes; 63 this.signal = opts.signal; 64 this.follow = !!opts.follow; 65 this.dot = !!opts.dot; 66 this.dotRelative = !!opts.dotRelative; 67 this.nodir = !!opts.nodir; 68 this.mark = !!opts.mark; 69 if (!opts.cwd) { 70 this.cwd = ''; 71 } 72 else if (opts.cwd instanceof URL || opts.cwd.startsWith('file://')) { 73 opts.cwd = fileURLToPath(opts.cwd); 74 } 75 this.cwd = opts.cwd || ''; 76 this.root = opts.root; 77 this.magicalBraces = !!opts.magicalBraces; 78 this.nobrace = !!opts.nobrace; 79 this.noext = !!opts.noext; 80 this.realpath = !!opts.realpath; 81 this.absolute = opts.absolute; 82 this.noglobstar = !!opts.noglobstar; 83 this.matchBase = !!opts.matchBase; 84 this.maxDepth = 85 typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity; 86 this.stat = !!opts.stat; 87 this.ignore = opts.ignore; 88 if (this.withFileTypes && this.absolute !== undefined) { 89 throw new Error('cannot set absolute and withFileTypes:true'); 90 } 91 if (typeof pattern === 'string') { 92 pattern = [pattern]; 93 } 94 this.windowsPathsNoEscape = 95 !!opts.windowsPathsNoEscape || 96 opts.allowWindowsEscape === false; 97 if (this.windowsPathsNoEscape) { 98 pattern = pattern.map(p => p.replace(/\\/g, '/')); 99 } 100 if (this.matchBase) { 101 if (opts.noglobstar) { 102 throw new TypeError('base matching requires globstar'); 103 } 104 pattern = pattern.map(p => (p.includes('/') ? p : `./**/${p}`)); 105 } 106 this.pattern = pattern; 107 this.platform = opts.platform || defaultPlatform; 108 this.opts = { ...opts, platform: this.platform }; 109 if (opts.scurry) { 110 this.scurry = opts.scurry; 111 if (opts.nocase !== undefined && 112 opts.nocase !== opts.scurry.nocase) { 113 throw new Error('nocase option contradicts provided scurry option'); 114 } 115 } 116 else { 117 const Scurry = opts.platform === 'win32' 118 ? PathScurryWin32 119 : opts.platform === 'darwin' 120 ? PathScurryDarwin 121 : opts.platform 122 ? PathScurryPosix 123 : PathScurry; 124 this.scurry = new Scurry(this.cwd, { 125 nocase: opts.nocase, 126 fs: opts.fs, 127 }); 128 } 129 this.nocase = this.scurry.nocase; 130 // If you do nocase:true on a case-sensitive file system, then 131 // we need to use regexps instead of strings for non-magic 132 // path portions, because statting `aBc` won't return results 133 // for the file `AbC` for example. 134 const nocaseMagicOnly = this.platform === 'darwin' || this.platform === 'win32'; 135 const mmo = { 136 // default nocase based on platform 137 ...opts, 138 dot: this.dot, 139 matchBase: this.matchBase, 140 nobrace: this.nobrace, 141 nocase: this.nocase, 142 nocaseMagicOnly, 143 nocomment: true, 144 noext: this.noext, 145 nonegate: true, 146 optimizationLevel: 2, 147 platform: this.platform, 148 windowsPathsNoEscape: this.windowsPathsNoEscape, 149 debug: !!this.opts.debug, 150 }; 151 const mms = this.pattern.map(p => new Minimatch(p, mmo)); 152 const [matchSet, globParts] = mms.reduce((set, m) => { 153 set[0].push(...m.set); 154 set[1].push(...m.globParts); 155 return set; 156 }, [[], []]); 157 this.patterns = matchSet.map((set, i) => { 158 return new Pattern(set, globParts[i], 0, this.platform); 159 }); 160 } 161 async walk() { 162 // Walkers always return array of Path objects, so we just have to 163 // coerce them into the right shape. It will have already called 164 // realpath() if the option was set to do so, so we know that's cached. 165 // start out knowing the cwd, at least 166 return [ 167 ...(await new GlobWalker(this.patterns, this.scurry.cwd, { 168 ...this.opts, 169 maxDepth: this.maxDepth !== Infinity 170 ? this.maxDepth + this.scurry.cwd.depth() 171 : Infinity, 172 platform: this.platform, 173 nocase: this.nocase, 174 }).walk()), 175 ]; 176 } 177 walkSync() { 178 return [ 179 ...new GlobWalker(this.patterns, this.scurry.cwd, { 180 ...this.opts, 181 maxDepth: this.maxDepth !== Infinity 182 ? this.maxDepth + this.scurry.cwd.depth() 183 : Infinity, 184 platform: this.platform, 185 nocase: this.nocase, 186 }).walkSync(), 187 ]; 188 } 189 stream() { 190 return new GlobStream(this.patterns, this.scurry.cwd, { 191 ...this.opts, 192 maxDepth: this.maxDepth !== Infinity 193 ? this.maxDepth + this.scurry.cwd.depth() 194 : Infinity, 195 platform: this.platform, 196 nocase: this.nocase, 197 }).stream(); 198 } 199 streamSync() { 200 return new GlobStream(this.patterns, this.scurry.cwd, { 201 ...this.opts, 202 maxDepth: this.maxDepth !== Infinity 203 ? this.maxDepth + this.scurry.cwd.depth() 204 : Infinity, 205 platform: this.platform, 206 nocase: this.nocase, 207 }).streamSync(); 208 } 209 /** 210 * Default sync iteration function. Returns a Generator that 211 * iterates over the results. 212 */ 213 iterateSync() { 214 return this.streamSync()[Symbol.iterator](); 215 } 216 [Symbol.iterator]() { 217 return this.iterateSync(); 218 } 219 /** 220 * Default async iteration function. Returns an AsyncGenerator that 221 * iterates over the results. 222 */ 223 iterate() { 224 return this.stream()[Symbol.asyncIterator](); 225 } 226 [Symbol.asyncIterator]() { 227 return this.iterate(); 228 } 229} 230//# sourceMappingURL=glob.js.map