1// this is just a very light wrapper around 2 arrays with an offset index 2import { GLOBSTAR } from 'minimatch'; 3const isPatternList = (pl) => pl.length >= 1; 4const isGlobList = (gl) => gl.length >= 1; 5/** 6 * An immutable-ish view on an array of glob parts and their parsed 7 * results 8 */ 9export class Pattern { 10 #patternList; 11 #globList; 12 #index; 13 length; 14 #platform; 15 #rest; 16 #globString; 17 #isDrive; 18 #isUNC; 19 #isAbsolute; 20 #followGlobstar = true; 21 constructor(patternList, globList, index, platform) { 22 if (!isPatternList(patternList)) { 23 throw new TypeError('empty pattern list'); 24 } 25 if (!isGlobList(globList)) { 26 throw new TypeError('empty glob list'); 27 } 28 if (globList.length !== patternList.length) { 29 throw new TypeError('mismatched pattern list and glob list lengths'); 30 } 31 this.length = patternList.length; 32 if (index < 0 || index >= this.length) { 33 throw new TypeError('index out of range'); 34 } 35 this.#patternList = patternList; 36 this.#globList = globList; 37 this.#index = index; 38 this.#platform = platform; 39 // normalize root entries of absolute patterns on initial creation. 40 if (this.#index === 0) { 41 // c: => ['c:/'] 42 // C:/ => ['C:/'] 43 // C:/x => ['C:/', 'x'] 44 // //host/share => ['//host/share/'] 45 // //host/share/ => ['//host/share/'] 46 // //host/share/x => ['//host/share/', 'x'] 47 // /etc => ['/', 'etc'] 48 // / => ['/'] 49 if (this.isUNC()) { 50 // '' / '' / 'host' / 'share' 51 const [p0, p1, p2, p3, ...prest] = this.#patternList; 52 const [g0, g1, g2, g3, ...grest] = this.#globList; 53 if (prest[0] === '') { 54 // ends in / 55 prest.shift(); 56 grest.shift(); 57 } 58 const p = [p0, p1, p2, p3, ''].join('/'); 59 const g = [g0, g1, g2, g3, ''].join('/'); 60 this.#patternList = [p, ...prest]; 61 this.#globList = [g, ...grest]; 62 this.length = this.#patternList.length; 63 } 64 else if (this.isDrive() || this.isAbsolute()) { 65 const [p1, ...prest] = this.#patternList; 66 const [g1, ...grest] = this.#globList; 67 if (prest[0] === '') { 68 // ends in / 69 prest.shift(); 70 grest.shift(); 71 } 72 const p = p1 + '/'; 73 const g = g1 + '/'; 74 this.#patternList = [p, ...prest]; 75 this.#globList = [g, ...grest]; 76 this.length = this.#patternList.length; 77 } 78 } 79 } 80 /** 81 * The first entry in the parsed list of patterns 82 */ 83 pattern() { 84 return this.#patternList[this.#index]; 85 } 86 /** 87 * true of if pattern() returns a string 88 */ 89 isString() { 90 return typeof this.#patternList[this.#index] === 'string'; 91 } 92 /** 93 * true of if pattern() returns GLOBSTAR 94 */ 95 isGlobstar() { 96 return this.#patternList[this.#index] === GLOBSTAR; 97 } 98 /** 99 * true if pattern() returns a regexp 100 */ 101 isRegExp() { 102 return this.#patternList[this.#index] instanceof RegExp; 103 } 104 /** 105 * The /-joined set of glob parts that make up this pattern 106 */ 107 globString() { 108 return (this.#globString = 109 this.#globString || 110 (this.#index === 0 111 ? this.isAbsolute() 112 ? this.#globList[0] + this.#globList.slice(1).join('/') 113 : this.#globList.join('/') 114 : this.#globList.slice(this.#index).join('/'))); 115 } 116 /** 117 * true if there are more pattern parts after this one 118 */ 119 hasMore() { 120 return this.length > this.#index + 1; 121 } 122 /** 123 * The rest of the pattern after this part, or null if this is the end 124 */ 125 rest() { 126 if (this.#rest !== undefined) 127 return this.#rest; 128 if (!this.hasMore()) 129 return (this.#rest = null); 130 this.#rest = new Pattern(this.#patternList, this.#globList, this.#index + 1, this.#platform); 131 this.#rest.#isAbsolute = this.#isAbsolute; 132 this.#rest.#isUNC = this.#isUNC; 133 this.#rest.#isDrive = this.#isDrive; 134 return this.#rest; 135 } 136 /** 137 * true if the pattern represents a //unc/path/ on windows 138 */ 139 isUNC() { 140 const pl = this.#patternList; 141 return this.#isUNC !== undefined 142 ? this.#isUNC 143 : (this.#isUNC = 144 this.#platform === 'win32' && 145 this.#index === 0 && 146 pl[0] === '' && 147 pl[1] === '' && 148 typeof pl[2] === 'string' && 149 !!pl[2] && 150 typeof pl[3] === 'string' && 151 !!pl[3]); 152 } 153 // pattern like C:/... 154 // split = ['C:', ...] 155 // XXX: would be nice to handle patterns like `c:*` to test the cwd 156 // in c: for *, but I don't know of a way to even figure out what that 157 // cwd is without actually chdir'ing into it? 158 /** 159 * True if the pattern starts with a drive letter on Windows 160 */ 161 isDrive() { 162 const pl = this.#patternList; 163 return this.#isDrive !== undefined 164 ? this.#isDrive 165 : (this.#isDrive = 166 this.#platform === 'win32' && 167 this.#index === 0 && 168 this.length > 1 && 169 typeof pl[0] === 'string' && 170 /^[a-z]:$/i.test(pl[0])); 171 } 172 // pattern = '/' or '/...' or '/x/...' 173 // split = ['', ''] or ['', ...] or ['', 'x', ...] 174 // Drive and UNC both considered absolute on windows 175 /** 176 * True if the pattern is rooted on an absolute path 177 */ 178 isAbsolute() { 179 const pl = this.#patternList; 180 return this.#isAbsolute !== undefined 181 ? this.#isAbsolute 182 : (this.#isAbsolute = 183 (pl[0] === '' && pl.length > 1) || 184 this.isDrive() || 185 this.isUNC()); 186 } 187 /** 188 * consume the root of the pattern, and return it 189 */ 190 root() { 191 const p = this.#patternList[0]; 192 return typeof p === 'string' && this.isAbsolute() && this.#index === 0 193 ? p 194 : ''; 195 } 196 /** 197 * Check to see if the current globstar pattern is allowed to follow 198 * a symbolic link. 199 */ 200 checkFollowGlobstar() { 201 return !(this.#index === 0 || 202 !this.isGlobstar() || 203 !this.#followGlobstar); 204 } 205 /** 206 * Mark that the current globstar pattern is following a symbolic link 207 */ 208 markFollowGlobstar() { 209 if (this.#index === 0 || !this.isGlobstar() || !this.#followGlobstar) 210 return false; 211 this.#followGlobstar = false; 212 return true; 213 } 214} 215//# sourceMappingURL=pattern.js.map