Spaces:
Sleeping
Sleeping
// this is just a very light wrapper around 2 arrays with an offset index | |
import { GLOBSTAR } from 'minimatch'; | |
const isPatternList = (pl) => pl.length >= 1; | |
const isGlobList = (gl) => gl.length >= 1; | |
/** | |
* An immutable-ish view on an array of glob parts and their parsed | |
* results | |
*/ | |
export class Pattern { | |
#patternList; | |
#globList; | |
#index; | |
length; | |
#platform; | |
#rest; | |
#globString; | |
#isDrive; | |
#isUNC; | |
#isAbsolute; | |
#followGlobstar = true; | |
constructor(patternList, globList, index, platform) { | |
if (!isPatternList(patternList)) { | |
throw new TypeError('empty pattern list'); | |
} | |
if (!isGlobList(globList)) { | |
throw new TypeError('empty glob list'); | |
} | |
if (globList.length !== patternList.length) { | |
throw new TypeError('mismatched pattern list and glob list lengths'); | |
} | |
this.length = patternList.length; | |
if (index < 0 || index >= this.length) { | |
throw new TypeError('index out of range'); | |
} | |
this.#patternList = patternList; | |
this.#globList = globList; | |
this.#index = index; | |
this.#platform = platform; | |
// normalize root entries of absolute patterns on initial creation. | |
if (this.#index === 0) { | |
// c: => ['c:/'] | |
// C:/ => ['C:/'] | |
// C:/x => ['C:/', 'x'] | |
// //host/share => ['//host/share/'] | |
// //host/share/ => ['//host/share/'] | |
// //host/share/x => ['//host/share/', 'x'] | |
// /etc => ['/', 'etc'] | |
// / => ['/'] | |
if (this.isUNC()) { | |
// '' / '' / 'host' / 'share' | |
const [p0, p1, p2, p3, ...prest] = this.#patternList; | |
const [g0, g1, g2, g3, ...grest] = this.#globList; | |
if (prest[0] === '') { | |
// ends in / | |
prest.shift(); | |
grest.shift(); | |
} | |
const p = [p0, p1, p2, p3, ''].join('/'); | |
const g = [g0, g1, g2, g3, ''].join('/'); | |
this.#patternList = [p, ...prest]; | |
this.#globList = [g, ...grest]; | |
this.length = this.#patternList.length; | |
} | |
else if (this.isDrive() || this.isAbsolute()) { | |
const [p1, ...prest] = this.#patternList; | |
const [g1, ...grest] = this.#globList; | |
if (prest[0] === '') { | |
// ends in / | |
prest.shift(); | |
grest.shift(); | |
} | |
const p = p1 + '/'; | |
const g = g1 + '/'; | |
this.#patternList = [p, ...prest]; | |
this.#globList = [g, ...grest]; | |
this.length = this.#patternList.length; | |
} | |
} | |
} | |
/** | |
* The first entry in the parsed list of patterns | |
*/ | |
pattern() { | |
return this.#patternList[this.#index]; | |
} | |
/** | |
* true of if pattern() returns a string | |
*/ | |
isString() { | |
return typeof this.#patternList[this.#index] === 'string'; | |
} | |
/** | |
* true of if pattern() returns GLOBSTAR | |
*/ | |
isGlobstar() { | |
return this.#patternList[this.#index] === GLOBSTAR; | |
} | |
/** | |
* true if pattern() returns a regexp | |
*/ | |
isRegExp() { | |
return this.#patternList[this.#index] instanceof RegExp; | |
} | |
/** | |
* The /-joined set of glob parts that make up this pattern | |
*/ | |
globString() { | |
return (this.#globString = | |
this.#globString || | |
(this.#index === 0 ? | |
this.isAbsolute() ? | |
this.#globList[0] + this.#globList.slice(1).join('/') | |
: this.#globList.join('/') | |
: this.#globList.slice(this.#index).join('/'))); | |
} | |
/** | |
* true if there are more pattern parts after this one | |
*/ | |
hasMore() { | |
return this.length > this.#index + 1; | |
} | |
/** | |
* The rest of the pattern after this part, or null if this is the end | |
*/ | |
rest() { | |
if (this.#rest !== undefined) | |
return this.#rest; | |
if (!this.hasMore()) | |
return (this.#rest = null); | |
this.#rest = new Pattern(this.#patternList, this.#globList, this.#index + 1, this.#platform); | |
this.#rest.#isAbsolute = this.#isAbsolute; | |
this.#rest.#isUNC = this.#isUNC; | |
this.#rest.#isDrive = this.#isDrive; | |
return this.#rest; | |
} | |
/** | |
* true if the pattern represents a //unc/path/ on windows | |
*/ | |
isUNC() { | |
const pl = this.#patternList; | |
return this.#isUNC !== undefined ? | |
this.#isUNC | |
: (this.#isUNC = | |
this.#platform === 'win32' && | |
this.#index === 0 && | |
pl[0] === '' && | |
pl[1] === '' && | |
typeof pl[2] === 'string' && | |
!!pl[2] && | |
typeof pl[3] === 'string' && | |
!!pl[3]); | |
} | |
// pattern like C:/... | |
// split = ['C:', ...] | |
// XXX: would be nice to handle patterns like `c:*` to test the cwd | |
// in c: for *, but I don't know of a way to even figure out what that | |
// cwd is without actually chdir'ing into it? | |
/** | |
* True if the pattern starts with a drive letter on Windows | |
*/ | |
isDrive() { | |
const pl = this.#patternList; | |
return this.#isDrive !== undefined ? | |
this.#isDrive | |
: (this.#isDrive = | |
this.#platform === 'win32' && | |
this.#index === 0 && | |
this.length > 1 && | |
typeof pl[0] === 'string' && | |
/^[a-z]:$/i.test(pl[0])); | |
} | |
// pattern = '/' or '/...' or '/x/...' | |
// split = ['', ''] or ['', ...] or ['', 'x', ...] | |
// Drive and UNC both considered absolute on windows | |
/** | |
* True if the pattern is rooted on an absolute path | |
*/ | |
isAbsolute() { | |
const pl = this.#patternList; | |
return this.#isAbsolute !== undefined ? | |
this.#isAbsolute | |
: (this.#isAbsolute = | |
(pl[0] === '' && pl.length > 1) || | |
this.isDrive() || | |
this.isUNC()); | |
} | |
/** | |
* consume the root of the pattern, and return it | |
*/ | |
root() { | |
const p = this.#patternList[0]; | |
return (typeof p === 'string' && this.isAbsolute() && this.#index === 0) ? | |
p | |
: ''; | |
} | |
/** | |
* Check to see if the current globstar pattern is allowed to follow | |
* a symbolic link. | |
*/ | |
checkFollowGlobstar() { | |
return !(this.#index === 0 || | |
!this.isGlobstar() || | |
!this.#followGlobstar); | |
} | |
/** | |
* Mark that the current globstar pattern is following a symbolic link | |
*/ | |
markFollowGlobstar() { | |
if (this.#index === 0 || !this.isGlobstar() || !this.#followGlobstar) | |
return false; | |
this.#followGlobstar = false; | |
return true; | |
} | |
} | |
//# sourceMappingURL=pattern.js.map |