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 /* c8 ignore start */ 63 if (!opts) 64 throw new TypeError('glob options required'); 65 /* c8 ignore stop */ 66 this.withFileTypes = !!opts.withFileTypes; 67 this.signal = opts.signal; 68 this.follow = !!opts.follow; 69 this.dot = !!opts.dot; 70 this.dotRelative = !!opts.dotRelative; 71 this.nodir = !!opts.nodir; 72 this.mark = !!opts.mark; 73 if (!opts.cwd) { 74 this.cwd = ''; 75 } 76 else if (opts.cwd instanceof URL || opts.cwd.startsWith('file://')) { 77 opts.cwd = fileURLToPath(opts.cwd); 78 } 79 this.cwd = opts.cwd || ''; 80 this.root = opts.root; 81 this.magicalBraces = !!opts.magicalBraces; 82 this.nobrace = !!opts.nobrace; 83 this.noext = !!opts.noext; 84 this.realpath = !!opts.realpath; 85 this.absolute = opts.absolute; 86 this.noglobstar = !!opts.noglobstar; 87 this.matchBase = !!opts.matchBase; 88 this.maxDepth = 89 typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity; 90 this.stat = !!opts.stat; 91 this.ignore = opts.ignore; 92 if (this.withFileTypes && this.absolute !== undefined) { 93 throw new Error('cannot set absolute and withFileTypes:true'); 94 } 95 if (typeof pattern === 'string') { 96 pattern = [pattern]; 97 } 98 this.windowsPathsNoEscape = 99 !!opts.windowsPathsNoEscape || 100 opts.allowWindowsEscape === false; 101 if (this.windowsPathsNoEscape) { 102 pattern = pattern.map(p => p.replace(/\\/g, '/')); 103 } 104 if (this.matchBase) { 105 if (opts.noglobstar) { 106 throw new TypeError('base matching requires globstar'); 107 } 108 pattern = pattern.map(p => (p.includes('/') ? p : `./**/${p}`)); 109 } 110 this.pattern = pattern; 111 this.platform = opts.platform || defaultPlatform; 112 this.opts = { ...opts, platform: this.platform }; 113 if (opts.scurry) { 114 this.scurry = opts.scurry; 115 if (opts.nocase !== undefined && 116 opts.nocase !== opts.scurry.nocase) { 117 throw new Error('nocase option contradicts provided scurry option'); 118 } 119 } 120 else { 121 const Scurry = opts.platform === 'win32' 122 ? PathScurryWin32 123 : opts.platform === 'darwin' 124 ? PathScurryDarwin 125 : opts.platform 126 ? PathScurryPosix 127 : PathScurry; 128 this.scurry = new Scurry(this.cwd, { 129 nocase: opts.nocase, 130 fs: opts.fs, 131 }); 132 } 133 this.nocase = this.scurry.nocase; 134 // If you do nocase:true on a case-sensitive file system, then 135 // we need to use regexps instead of strings for non-magic 136 // path portions, because statting `aBc` won't return results 137 // for the file `AbC` for example. 138 const nocaseMagicOnly = this.platform === 'darwin' || this.platform === 'win32'; 139 const mmo = { 140 // default nocase based on platform 141 ...opts, 142 dot: this.dot, 143 matchBase: this.matchBase, 144 nobrace: this.nobrace, 145 nocase: this.nocase, 146 nocaseMagicOnly, 147 nocomment: true, 148 noext: this.noext, 149 nonegate: true, 150 optimizationLevel: 2, 151 platform: this.platform, 152 windowsPathsNoEscape: this.windowsPathsNoEscape, 153 debug: !!this.opts.debug, 154 }; 155 const mms = this.pattern.map(p => new Minimatch(p, mmo)); 156 const [matchSet, globParts] = mms.reduce((set, m) => { 157 set[0].push(...m.set); 158 set[1].push(...m.globParts); 159 return set; 160 }, [[], []]); 161 this.patterns = matchSet.map((set, i) => { 162 const g = globParts[i]; 163 /* c8 ignore start */ 164 if (!g) 165 throw new Error('invalid pattern object'); 166 /* c8 ignore stop */ 167 return new Pattern(set, g, 0, this.platform); 168 }); 169 } 170 async walk() { 171 // Walkers always return array of Path objects, so we just have to 172 // coerce them into the right shape. It will have already called 173 // realpath() if the option was set to do so, so we know that's cached. 174 // start out knowing the cwd, at least 175 return [ 176 ...(await new GlobWalker(this.patterns, this.scurry.cwd, { 177 ...this.opts, 178 maxDepth: this.maxDepth !== Infinity 179 ? this.maxDepth + this.scurry.cwd.depth() 180 : Infinity, 181 platform: this.platform, 182 nocase: this.nocase, 183 }).walk()), 184 ]; 185 } 186 walkSync() { 187 return [ 188 ...new GlobWalker(this.patterns, this.scurry.cwd, { 189 ...this.opts, 190 maxDepth: this.maxDepth !== Infinity 191 ? this.maxDepth + this.scurry.cwd.depth() 192 : Infinity, 193 platform: this.platform, 194 nocase: this.nocase, 195 }).walkSync(), 196 ]; 197 } 198 stream() { 199 return new GlobStream(this.patterns, this.scurry.cwd, { 200 ...this.opts, 201 maxDepth: this.maxDepth !== Infinity 202 ? this.maxDepth + this.scurry.cwd.depth() 203 : Infinity, 204 platform: this.platform, 205 nocase: this.nocase, 206 }).stream(); 207 } 208 streamSync() { 209 return new GlobStream(this.patterns, this.scurry.cwd, { 210 ...this.opts, 211 maxDepth: this.maxDepth !== Infinity 212 ? this.maxDepth + this.scurry.cwd.depth() 213 : Infinity, 214 platform: this.platform, 215 nocase: this.nocase, 216 }).streamSync(); 217 } 218 /** 219 * Default sync iteration function. Returns a Generator that 220 * iterates over the results. 221 */ 222 iterateSync() { 223 return this.streamSync()[Symbol.iterator](); 224 } 225 [Symbol.iterator]() { 226 return this.iterateSync(); 227 } 228 /** 229 * Default async iteration function. Returns an AsyncGenerator that 230 * iterates over the results. 231 */ 232 iterate() { 233 return this.stream()[Symbol.asyncIterator](); 234 } 235 [Symbol.asyncIterator]() { 236 return this.iterate(); 237 } 238} 239//# sourceMappingURL=glob.js.map