You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
8.1 KiB
243 lines
8.1 KiB
import { Minimatch } from 'minimatch'; |
|
import { fileURLToPath } from 'node:url'; |
|
import { PathScurry, PathScurryDarwin, PathScurryPosix, PathScurryWin32, } from 'path-scurry'; |
|
import { Pattern } from './pattern.js'; |
|
import { GlobStream, GlobWalker } from './walker.js'; |
|
// if no process global, just call it linux. |
|
// so we default to case-sensitive, / separators |
|
const defaultPlatform = (typeof process === 'object' && |
|
process && |
|
typeof process.platform === 'string') ? |
|
process.platform |
|
: 'linux'; |
|
/** |
|
* An object that can perform glob pattern traversals. |
|
*/ |
|
export class Glob { |
|
absolute; |
|
cwd; |
|
root; |
|
dot; |
|
dotRelative; |
|
follow; |
|
ignore; |
|
magicalBraces; |
|
mark; |
|
matchBase; |
|
maxDepth; |
|
nobrace; |
|
nocase; |
|
nodir; |
|
noext; |
|
noglobstar; |
|
pattern; |
|
platform; |
|
realpath; |
|
scurry; |
|
stat; |
|
signal; |
|
windowsPathsNoEscape; |
|
withFileTypes; |
|
includeChildMatches; |
|
/** |
|
* The options provided to the constructor. |
|
*/ |
|
opts; |
|
/** |
|
* An array of parsed immutable {@link Pattern} objects. |
|
*/ |
|
patterns; |
|
/** |
|
* All options are stored as properties on the `Glob` object. |
|
* |
|
* See {@link GlobOptions} for full options descriptions. |
|
* |
|
* Note that a previous `Glob` object can be passed as the |
|
* `GlobOptions` to another `Glob` instantiation to re-use settings |
|
* and caches with a new pattern. |
|
* |
|
* Traversal functions can be called multiple times to run the walk |
|
* again. |
|
*/ |
|
constructor(pattern, opts) { |
|
/* c8 ignore start */ |
|
if (!opts) |
|
throw new TypeError('glob options required'); |
|
/* c8 ignore stop */ |
|
this.withFileTypes = !!opts.withFileTypes; |
|
this.signal = opts.signal; |
|
this.follow = !!opts.follow; |
|
this.dot = !!opts.dot; |
|
this.dotRelative = !!opts.dotRelative; |
|
this.nodir = !!opts.nodir; |
|
this.mark = !!opts.mark; |
|
if (!opts.cwd) { |
|
this.cwd = ''; |
|
} |
|
else if (opts.cwd instanceof URL || opts.cwd.startsWith('file://')) { |
|
opts.cwd = fileURLToPath(opts.cwd); |
|
} |
|
this.cwd = opts.cwd || ''; |
|
this.root = opts.root; |
|
this.magicalBraces = !!opts.magicalBraces; |
|
this.nobrace = !!opts.nobrace; |
|
this.noext = !!opts.noext; |
|
this.realpath = !!opts.realpath; |
|
this.absolute = opts.absolute; |
|
this.includeChildMatches = opts.includeChildMatches !== false; |
|
this.noglobstar = !!opts.noglobstar; |
|
this.matchBase = !!opts.matchBase; |
|
this.maxDepth = |
|
typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity; |
|
this.stat = !!opts.stat; |
|
this.ignore = opts.ignore; |
|
if (this.withFileTypes && this.absolute !== undefined) { |
|
throw new Error('cannot set absolute and withFileTypes:true'); |
|
} |
|
if (typeof pattern === 'string') { |
|
pattern = [pattern]; |
|
} |
|
this.windowsPathsNoEscape = |
|
!!opts.windowsPathsNoEscape || |
|
opts.allowWindowsEscape === |
|
false; |
|
if (this.windowsPathsNoEscape) { |
|
pattern = pattern.map(p => p.replace(/\\/g, '/')); |
|
} |
|
if (this.matchBase) { |
|
if (opts.noglobstar) { |
|
throw new TypeError('base matching requires globstar'); |
|
} |
|
pattern = pattern.map(p => (p.includes('/') ? p : `./**/${p}`)); |
|
} |
|
this.pattern = pattern; |
|
this.platform = opts.platform || defaultPlatform; |
|
this.opts = { ...opts, platform: this.platform }; |
|
if (opts.scurry) { |
|
this.scurry = opts.scurry; |
|
if (opts.nocase !== undefined && |
|
opts.nocase !== opts.scurry.nocase) { |
|
throw new Error('nocase option contradicts provided scurry option'); |
|
} |
|
} |
|
else { |
|
const Scurry = opts.platform === 'win32' ? PathScurryWin32 |
|
: opts.platform === 'darwin' ? PathScurryDarwin |
|
: opts.platform ? PathScurryPosix |
|
: PathScurry; |
|
this.scurry = new Scurry(this.cwd, { |
|
nocase: opts.nocase, |
|
fs: opts.fs, |
|
}); |
|
} |
|
this.nocase = this.scurry.nocase; |
|
// If you do nocase:true on a case-sensitive file system, then |
|
// we need to use regexps instead of strings for non-magic |
|
// path portions, because statting `aBc` won't return results |
|
// for the file `AbC` for example. |
|
const nocaseMagicOnly = this.platform === 'darwin' || this.platform === 'win32'; |
|
const mmo = { |
|
// default nocase based on platform |
|
...opts, |
|
dot: this.dot, |
|
matchBase: this.matchBase, |
|
nobrace: this.nobrace, |
|
nocase: this.nocase, |
|
nocaseMagicOnly, |
|
nocomment: true, |
|
noext: this.noext, |
|
nonegate: true, |
|
optimizationLevel: 2, |
|
platform: this.platform, |
|
windowsPathsNoEscape: this.windowsPathsNoEscape, |
|
debug: !!this.opts.debug, |
|
}; |
|
const mms = this.pattern.map(p => new Minimatch(p, mmo)); |
|
const [matchSet, globParts] = mms.reduce((set, m) => { |
|
set[0].push(...m.set); |
|
set[1].push(...m.globParts); |
|
return set; |
|
}, [[], []]); |
|
this.patterns = matchSet.map((set, i) => { |
|
const g = globParts[i]; |
|
/* c8 ignore start */ |
|
if (!g) |
|
throw new Error('invalid pattern object'); |
|
/* c8 ignore stop */ |
|
return new Pattern(set, g, 0, this.platform); |
|
}); |
|
} |
|
async walk() { |
|
// Walkers always return array of Path objects, so we just have to |
|
// coerce them into the right shape. It will have already called |
|
// realpath() if the option was set to do so, so we know that's cached. |
|
// start out knowing the cwd, at least |
|
return [ |
|
...(await new GlobWalker(this.patterns, this.scurry.cwd, { |
|
...this.opts, |
|
maxDepth: this.maxDepth !== Infinity ? |
|
this.maxDepth + this.scurry.cwd.depth() |
|
: Infinity, |
|
platform: this.platform, |
|
nocase: this.nocase, |
|
includeChildMatches: this.includeChildMatches, |
|
}).walk()), |
|
]; |
|
} |
|
walkSync() { |
|
return [ |
|
...new GlobWalker(this.patterns, this.scurry.cwd, { |
|
...this.opts, |
|
maxDepth: this.maxDepth !== Infinity ? |
|
this.maxDepth + this.scurry.cwd.depth() |
|
: Infinity, |
|
platform: this.platform, |
|
nocase: this.nocase, |
|
includeChildMatches: this.includeChildMatches, |
|
}).walkSync(), |
|
]; |
|
} |
|
stream() { |
|
return new GlobStream(this.patterns, this.scurry.cwd, { |
|
...this.opts, |
|
maxDepth: this.maxDepth !== Infinity ? |
|
this.maxDepth + this.scurry.cwd.depth() |
|
: Infinity, |
|
platform: this.platform, |
|
nocase: this.nocase, |
|
includeChildMatches: this.includeChildMatches, |
|
}).stream(); |
|
} |
|
streamSync() { |
|
return new GlobStream(this.patterns, this.scurry.cwd, { |
|
...this.opts, |
|
maxDepth: this.maxDepth !== Infinity ? |
|
this.maxDepth + this.scurry.cwd.depth() |
|
: Infinity, |
|
platform: this.platform, |
|
nocase: this.nocase, |
|
includeChildMatches: this.includeChildMatches, |
|
}).streamSync(); |
|
} |
|
/** |
|
* Default sync iteration function. Returns a Generator that |
|
* iterates over the results. |
|
*/ |
|
iterateSync() { |
|
return this.streamSync()[Symbol.iterator](); |
|
} |
|
[Symbol.iterator]() { |
|
return this.iterateSync(); |
|
} |
|
/** |
|
* Default async iteration function. Returns an AsyncGenerator that |
|
* iterates over the results. |
|
*/ |
|
iterate() { |
|
return this.stream()[Symbol.asyncIterator](); |
|
} |
|
[Symbol.asyncIterator]() { |
|
return this.iterate(); |
|
} |
|
} |
|
//# sourceMappingURL=glob.js.map
|