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