source: node_modules/minimatch/dist/commonjs/ast.js@ 33517cc

finki-main main
Last change on this file since 33517cc was 33517cc, checked in by Klimentina Efremova <klimentina08642@…>, 10 days ago

Turned database from SQLite to PostgressSQL, updated database changes from Phase 1 and 2

  • Property mode set to 100644
File size: 29.9 KB
RevLine 
[81bc7da]1"use strict";
2// parse a single path portion
[33517cc]3var _a;
[81bc7da]4Object.defineProperty(exports, "__esModule", { value: true });
5exports.AST = void 0;
6const brace_expressions_js_1 = require("./brace-expressions.js");
7const unescape_js_1 = require("./unescape.js");
8const types = new Set(['!', '?', '+', '*', '@']);
9const isExtglobType = (c) => types.has(c);
[33517cc]10const isExtglobAST = (c) => isExtglobType(c.type);
11// Map of which extglob types can adopt the children of a nested extglob
12//
13// anything but ! can adopt a matching type:
14// +(a|+(b|c)|d) => +(a|b|c|d)
15// *(a|*(b|c)|d) => *(a|b|c|d)
16// @(a|@(b|c)|d) => @(a|b|c|d)
17// ?(a|?(b|c)|d) => ?(a|b|c|d)
18//
19// * can adopt anything, because 0 or repetition is allowed
20// *(a|?(b|c)|d) => *(a|b|c|d)
21// *(a|+(b|c)|d) => *(a|b|c|d)
22// *(a|@(b|c)|d) => *(a|b|c|d)
23//
24// + can adopt @, because 1 or repetition is allowed
25// +(a|@(b|c)|d) => +(a|b|c|d)
26//
27// + and @ CANNOT adopt *, because 0 would be allowed
28// +(a|*(b|c)|d) => would match "", on *(b|c)
29// @(a|*(b|c)|d) => would match "", on *(b|c)
30//
31// + and @ CANNOT adopt ?, because 0 would be allowed
32// +(a|?(b|c)|d) => would match "", on ?(b|c)
33// @(a|?(b|c)|d) => would match "", on ?(b|c)
34//
35// ? can adopt @, because 0 or 1 is allowed
36// ?(a|@(b|c)|d) => ?(a|b|c|d)
37//
38// ? and @ CANNOT adopt * or +, because >1 would be allowed
39// ?(a|*(b|c)|d) => would match bbb on *(b|c)
40// @(a|*(b|c)|d) => would match bbb on *(b|c)
41// ?(a|+(b|c)|d) => would match bbb on +(b|c)
42// @(a|+(b|c)|d) => would match bbb on +(b|c)
43//
44// ! CANNOT adopt ! (nothing else can either)
45// !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
46//
47// ! can adopt @
48// !(a|@(b|c)|d) => !(a|b|c|d)
49//
50// ! CANNOT adopt *
51// !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
52//
53// ! CANNOT adopt +
54// !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
55//
56// ! CANNOT adopt ?
57// x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
58const adoptionMap = new Map([
59 ['!', ['@']],
60 ['?', ['?', '@']],
61 ['@', ['@']],
62 ['*', ['*', '+', '?', '@']],
63 ['+', ['+', '@']],
64]);
65// nested extglobs that can be adopted in, but with the addition of
66// a blank '' element.
67const adoptionWithSpaceMap = new Map([
68 ['!', ['?']],
69 ['@', ['?']],
70 ['+', ['?', '*']],
71]);
72// union of the previous two maps
73const adoptionAnyMap = new Map([
74 ['!', ['?', '@']],
75 ['?', ['?', '@']],
76 ['@', ['?', '@']],
77 ['*', ['*', '+', '?', '@']],
78 ['+', ['+', '@', '?', '*']],
79]);
80// Extglobs that can take over their parent if they are the only child
81// the key is parent, value maps child to resulting extglob parent type
82// '@' is omitted because it's a special case. An `@` extglob with a single
83// member can always be usurped by that subpattern.
84const usurpMap = new Map([
85 ['!', new Map([['!', '@']])],
86 [
87 '?',
88 new Map([
89 ['*', '*'],
90 ['+', '*'],
91 ]),
92 ],
93 [
94 '@',
95 new Map([
96 ['!', '!'],
97 ['?', '?'],
98 ['@', '@'],
99 ['*', '*'],
100 ['+', '+'],
101 ]),
102 ],
103 [
104 '+',
105 new Map([
106 ['?', '*'],
107 ['*', '*'],
108 ]),
109 ],
110]);
[81bc7da]111// Patterns that get prepended to bind to the start of either the
112// entire string, or just a single path portion, to prevent dots
113// and/or traversal patterns, when needed.
114// Exts don't need the ^ or / bit, because the root binds that already.
115const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
116const startNoDot = '(?!\\.)';
117// characters that indicate a start of pattern needs the "no dots" bit,
118// because a dot *might* be matched. ( is not in the list, because in
119// the case of a child extglob, it will handle the prevention itself.
120const addPatternStart = new Set(['[', '.']);
121// cases where traversal is A-OK, no dot prevention needed
122const justDots = new Set(['..', '.']);
123const reSpecials = new Set('().*{}+?[]^$\\!');
124const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
125// any single thing other than /
126const qmark = '[^/]';
127// * => any number of characters
128const star = qmark + '*?';
129// use + when we need to ensure that *something* matches, because the * is
130// the only thing in the path portion.
131const starNoEmpty = qmark + '+?';
132// remove the \ chars that we added if we end up doing a nonmagic compare
133// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
[33517cc]134let ID = 0;
[81bc7da]135class AST {
136 type;
137 #root;
138 #hasMagic;
139 #uflag = false;
140 #parts = [];
141 #parent;
142 #parentIndex;
143 #negs;
144 #filledNegs = false;
145 #options;
146 #toString;
147 // set to true if it's an extglob with no children
148 // (which really means one child of '')
149 #emptyExt = false;
[33517cc]150 id = ++ID;
151 get depth() {
152 return (this.#parent?.depth ?? -1) + 1;
153 }
154 [Symbol.for('nodejs.util.inspect.custom')]() {
155 return {
156 '@@type': 'AST',
157 id: this.id,
158 type: this.type,
159 root: this.#root.id,
160 parent: this.#parent?.id,
161 depth: this.depth,
162 partsLength: this.#parts.length,
163 parts: this.#parts,
164 };
165 }
[81bc7da]166 constructor(type, parent, options = {}) {
167 this.type = type;
168 // extglobs are inherently magical
169 if (type)
170 this.#hasMagic = true;
171 this.#parent = parent;
172 this.#root = this.#parent ? this.#parent.#root : this;
173 this.#options = this.#root === this ? options : this.#root.#options;
174 this.#negs = this.#root === this ? [] : this.#root.#negs;
175 if (type === '!' && !this.#root.#filledNegs)
176 this.#negs.push(this);
177 this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
178 }
179 get hasMagic() {
180 /* c8 ignore start */
181 if (this.#hasMagic !== undefined)
182 return this.#hasMagic;
183 /* c8 ignore stop */
184 for (const p of this.#parts) {
185 if (typeof p === 'string')
186 continue;
187 if (p.type || p.hasMagic)
188 return (this.#hasMagic = true);
189 }
190 // note: will be undefined until we generate the regexp src and find out
191 return this.#hasMagic;
192 }
193 // reconstructs the pattern
194 toString() {
[33517cc]195 return (this.#toString !== undefined ? this.#toString
196 : !this.type ?
197 (this.#toString = this.#parts.map(p => String(p)).join(''))
198 : (this.#toString =
199 this.type +
200 '(' +
201 this.#parts.map(p => String(p)).join('|') +
202 ')'));
[81bc7da]203 }
204 #fillNegs() {
205 /* c8 ignore start */
206 if (this !== this.#root)
207 throw new Error('should only call on root');
208 if (this.#filledNegs)
209 return this;
210 /* c8 ignore stop */
211 // call toString() once to fill this out
212 this.toString();
213 this.#filledNegs = true;
214 let n;
215 while ((n = this.#negs.pop())) {
216 if (n.type !== '!')
217 continue;
218 // walk up the tree, appending everthing that comes AFTER parentIndex
219 let p = n;
220 let pp = p.#parent;
221 while (pp) {
222 for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
223 for (const part of n.#parts) {
224 /* c8 ignore start */
225 if (typeof part === 'string') {
226 throw new Error('string part in extglob AST??');
227 }
228 /* c8 ignore stop */
229 part.copyIn(pp.#parts[i]);
230 }
231 }
232 p = pp;
233 pp = p.#parent;
234 }
235 }
236 return this;
237 }
238 push(...parts) {
239 for (const p of parts) {
240 if (p === '')
241 continue;
242 /* c8 ignore start */
243 if (typeof p !== 'string' &&
[33517cc]244 !(p instanceof _a && p.#parent === this)) {
[81bc7da]245 throw new Error('invalid part: ' + p);
246 }
247 /* c8 ignore stop */
248 this.#parts.push(p);
249 }
250 }
251 toJSON() {
252 const ret = this.type === null ?
253 this.#parts
254 .slice()
255 .map(p => (typeof p === 'string' ? p : p.toJSON()))
256 : [this.type, ...this.#parts.map(p => p.toJSON())];
257 if (this.isStart() && !this.type)
258 ret.unshift([]);
259 if (this.isEnd() &&
260 (this === this.#root ||
261 (this.#root.#filledNegs && this.#parent?.type === '!'))) {
262 ret.push({});
263 }
264 return ret;
265 }
266 isStart() {
267 if (this.#root === this)
268 return true;
269 // if (this.type) return !!this.#parent?.isStart()
270 if (!this.#parent?.isStart())
271 return false;
272 if (this.#parentIndex === 0)
273 return true;
274 // if everything AHEAD of this is a negation, then it's still the "start"
275 const p = this.#parent;
276 for (let i = 0; i < this.#parentIndex; i++) {
277 const pp = p.#parts[i];
[33517cc]278 if (!(pp instanceof _a && pp.type === '!')) {
[81bc7da]279 return false;
280 }
281 }
282 return true;
283 }
284 isEnd() {
285 if (this.#root === this)
286 return true;
287 if (this.#parent?.type === '!')
288 return true;
289 if (!this.#parent?.isEnd())
290 return false;
291 if (!this.type)
292 return this.#parent?.isEnd();
293 // if not root, it'll always have a parent
294 /* c8 ignore start */
295 const pl = this.#parent ? this.#parent.#parts.length : 0;
296 /* c8 ignore stop */
297 return this.#parentIndex === pl - 1;
298 }
299 copyIn(part) {
300 if (typeof part === 'string')
301 this.push(part);
302 else
303 this.push(part.clone(this));
304 }
305 clone(parent) {
[33517cc]306 const c = new _a(this.type, parent);
[81bc7da]307 for (const p of this.#parts) {
308 c.copyIn(p);
309 }
310 return c;
311 }
[33517cc]312 static #parseAST(str, ast, pos, opt, extDepth) {
313 const maxDepth = opt.maxExtglobRecursion ?? 2;
[81bc7da]314 let escaping = false;
315 let inBrace = false;
316 let braceStart = -1;
317 let braceNeg = false;
318 if (ast.type === null) {
319 // outside of a extglob, append until we find a start
320 let i = pos;
321 let acc = '';
322 while (i < str.length) {
323 const c = str.charAt(i++);
324 // still accumulate escapes at this point, but we do ignore
325 // starts that are escaped
326 if (escaping || c === '\\') {
327 escaping = !escaping;
328 acc += c;
329 continue;
330 }
331 if (inBrace) {
332 if (i === braceStart + 1) {
333 if (c === '^' || c === '!') {
334 braceNeg = true;
335 }
336 }
337 else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
338 inBrace = false;
339 }
340 acc += c;
341 continue;
342 }
343 else if (c === '[') {
344 inBrace = true;
345 braceStart = i;
346 braceNeg = false;
347 acc += c;
348 continue;
349 }
[33517cc]350 // we don't have to check for adoption here, because that's
351 // done at the other recursion point.
352 const doRecurse = !opt.noext &&
353 isExtglobType(c) &&
354 str.charAt(i) === '(' &&
355 extDepth <= maxDepth;
356 if (doRecurse) {
[81bc7da]357 ast.push(acc);
358 acc = '';
[33517cc]359 const ext = new _a(c, ast);
360 i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
[81bc7da]361 ast.push(ext);
362 continue;
363 }
364 acc += c;
365 }
366 ast.push(acc);
367 return i;
368 }
369 // some kind of extglob, pos is at the (
370 // find the next | or )
371 let i = pos + 1;
[33517cc]372 let part = new _a(null, ast);
[81bc7da]373 const parts = [];
374 let acc = '';
375 while (i < str.length) {
376 const c = str.charAt(i++);
377 // still accumulate escapes at this point, but we do ignore
378 // starts that are escaped
379 if (escaping || c === '\\') {
380 escaping = !escaping;
381 acc += c;
382 continue;
383 }
384 if (inBrace) {
385 if (i === braceStart + 1) {
386 if (c === '^' || c === '!') {
387 braceNeg = true;
388 }
389 }
390 else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
391 inBrace = false;
392 }
393 acc += c;
394 continue;
395 }
396 else if (c === '[') {
397 inBrace = true;
398 braceStart = i;
399 braceNeg = false;
400 acc += c;
401 continue;
402 }
[33517cc]403 const doRecurse = !opt.noext &&
404 isExtglobType(c) &&
405 str.charAt(i) === '(' &&
406 /* c8 ignore start - the maxDepth is sufficient here */
407 (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
408 /* c8 ignore stop */
409 if (doRecurse) {
410 const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
[81bc7da]411 part.push(acc);
412 acc = '';
[33517cc]413 const ext = new _a(c, part);
[81bc7da]414 part.push(ext);
[33517cc]415 i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
[81bc7da]416 continue;
417 }
418 if (c === '|') {
419 part.push(acc);
420 acc = '';
421 parts.push(part);
[33517cc]422 part = new _a(null, ast);
[81bc7da]423 continue;
424 }
425 if (c === ')') {
426 if (acc === '' && ast.#parts.length === 0) {
427 ast.#emptyExt = true;
428 }
429 part.push(acc);
430 acc = '';
431 ast.push(...parts, part);
432 return i;
433 }
434 acc += c;
435 }
436 // unfinished extglob
437 // if we got here, it was a malformed extglob! not an extglob, but
438 // maybe something else in there.
439 ast.type = null;
440 ast.#hasMagic = undefined;
441 ast.#parts = [str.substring(pos - 1)];
442 return i;
443 }
[33517cc]444 #canAdoptWithSpace(child) {
445 return this.#canAdopt(child, adoptionWithSpaceMap);
446 }
447 #canAdopt(child, map = adoptionMap) {
448 if (!child ||
449 typeof child !== 'object' ||
450 child.type !== null ||
451 child.#parts.length !== 1 ||
452 this.type === null) {
453 return false;
454 }
455 const gc = child.#parts[0];
456 if (!gc || typeof gc !== 'object' || gc.type === null) {
457 return false;
458 }
459 return this.#canAdoptType(gc.type, map);
460 }
461 #canAdoptType(c, map = adoptionAnyMap) {
462 return !!map.get(this.type)?.includes(c);
463 }
464 #adoptWithSpace(child, index) {
465 const gc = child.#parts[0];
466 const blank = new _a(null, gc, this.options);
467 blank.#parts.push('');
468 gc.push(blank);
469 this.#adopt(child, index);
470 }
471 #adopt(child, index) {
472 const gc = child.#parts[0];
473 this.#parts.splice(index, 1, ...gc.#parts);
474 for (const p of gc.#parts) {
475 if (typeof p === 'object')
476 p.#parent = this;
477 }
478 this.#toString = undefined;
479 }
480 #canUsurpType(c) {
481 const m = usurpMap.get(this.type);
482 return !!m?.has(c);
483 }
484 #canUsurp(child) {
485 if (!child ||
486 typeof child !== 'object' ||
487 child.type !== null ||
488 child.#parts.length !== 1 ||
489 this.type === null ||
490 this.#parts.length !== 1) {
491 return false;
492 }
493 const gc = child.#parts[0];
494 if (!gc || typeof gc !== 'object' || gc.type === null) {
495 return false;
496 }
497 return this.#canUsurpType(gc.type);
498 }
499 #usurp(child) {
500 const m = usurpMap.get(this.type);
501 const gc = child.#parts[0];
502 const nt = m?.get(gc.type);
503 /* c8 ignore start - impossible */
504 if (!nt)
505 return false;
506 /* c8 ignore stop */
507 this.#parts = gc.#parts;
508 for (const p of this.#parts) {
509 if (typeof p === 'object') {
510 p.#parent = this;
511 }
512 }
513 this.type = nt;
514 this.#toString = undefined;
515 this.#emptyExt = false;
516 }
[81bc7da]517 static fromGlob(pattern, options = {}) {
[33517cc]518 const ast = new _a(null, undefined, options);
519 _a.#parseAST(pattern, ast, 0, options, 0);
[81bc7da]520 return ast;
521 }
522 // returns the regular expression if there's magic, or the unescaped
523 // string if not.
524 toMMPattern() {
525 // should only be called on root
526 /* c8 ignore start */
527 if (this !== this.#root)
528 return this.#root.toMMPattern();
529 /* c8 ignore stop */
530 const glob = this.toString();
531 const [re, body, hasMagic, uflag] = this.toRegExpSource();
532 // if we're in nocase mode, and not nocaseMagicOnly, then we do
533 // still need a regular expression if we have to case-insensitively
534 // match capital/lowercase characters.
535 const anyMagic = hasMagic ||
536 this.#hasMagic ||
537 (this.#options.nocase &&
538 !this.#options.nocaseMagicOnly &&
539 glob.toUpperCase() !== glob.toLowerCase());
540 if (!anyMagic) {
541 return body;
542 }
543 const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
544 return Object.assign(new RegExp(`^${re}$`, flags), {
545 _src: re,
546 _glob: glob,
547 });
548 }
549 get options() {
550 return this.#options;
551 }
552 // returns the string match, the regexp source, whether there's magic
553 // in the regexp (so a regular expression is required) and whether or
554 // not the uflag is needed for the regular expression (for posix classes)
555 // TODO: instead of injecting the start/end at this point, just return
556 // the BODY of the regexp, along with the start/end portions suitable
557 // for binding the start/end in either a joined full-path makeRe context
558 // (where we bind to (^|/), or a standalone matchPart context (where
559 // we bind to ^, and not /). Otherwise slashes get duped!
560 //
561 // In part-matching mode, the start is:
562 // - if not isStart: nothing
563 // - if traversal possible, but not allowed: ^(?!\.\.?$)
564 // - if dots allowed or not possible: ^
565 // - if dots possible and not allowed: ^(?!\.)
566 // end is:
567 // - if not isEnd(): nothing
568 // - else: $
569 //
570 // In full-path matching mode, we put the slash at the START of the
571 // pattern, so start is:
572 // - if first pattern: same as part-matching mode
573 // - if not isStart(): nothing
574 // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
575 // - if dots allowed or not possible: /
576 // - if dots possible and not allowed: /(?!\.)
577 // end is:
578 // - if last pattern, same as part-matching mode
579 // - else nothing
580 //
581 // Always put the (?:$|/) on negated tails, though, because that has to be
582 // there to bind the end of the negated pattern portion, and it's easier to
583 // just stick it in now rather than try to inject it later in the middle of
584 // the pattern.
585 //
586 // We can just always return the same end, and leave it up to the caller
587 // to know whether it's going to be used joined or in parts.
588 // And, if the start is adjusted slightly, can do the same there:
589 // - if not isStart: nothing
590 // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
591 // - if dots allowed or not possible: (?:/|^)
592 // - if dots possible and not allowed: (?:/|^)(?!\.)
593 //
594 // But it's better to have a simpler binding without a conditional, for
595 // performance, so probably better to return both start options.
596 //
597 // Then the caller just ignores the end if it's not the first pattern,
598 // and the start always gets applied.
599 //
600 // But that's always going to be $ if it's the ending pattern, or nothing,
601 // so the caller can just attach $ at the end of the pattern when building.
602 //
603 // So the todo is:
604 // - better detect what kind of start is needed
605 // - return both flavors of starting pattern
606 // - attach $ at the end of the pattern when creating the actual RegExp
607 //
608 // Ah, but wait, no, that all only applies to the root when the first pattern
609 // is not an extglob. If the first pattern IS an extglob, then we need all
610 // that dot prevention biz to live in the extglob portions, because eg
611 // +(*|.x*) can match .xy but not .yx.
612 //
613 // So, return the two flavors if it's #root and the first child is not an
614 // AST, otherwise leave it to the child AST to handle it, and there,
615 // use the (?:^|/) style of start binding.
616 //
617 // Even simplified further:
618 // - Since the start for a join is eg /(?!\.) and the start for a part
619 // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
620 // or start or whatever) and prepend ^ or / at the Regexp construction.
621 toRegExpSource(allowDot) {
622 const dot = allowDot ?? !!this.#options.dot;
[33517cc]623 if (this.#root === this) {
624 this.#flatten();
[81bc7da]625 this.#fillNegs();
[33517cc]626 }
627 if (!isExtglobAST(this)) {
[81bc7da]628 const noEmpty = this.isStart() &&
629 this.isEnd() &&
630 !this.#parts.some(s => typeof s !== 'string');
631 const src = this.#parts
632 .map(p => {
633 const [re, _, hasMagic, uflag] = typeof p === 'string' ?
[33517cc]634 _a.#parseGlob(p, this.#hasMagic, noEmpty)
[81bc7da]635 : p.toRegExpSource(allowDot);
636 this.#hasMagic = this.#hasMagic || hasMagic;
637 this.#uflag = this.#uflag || uflag;
638 return re;
639 })
640 .join('');
641 let start = '';
642 if (this.isStart()) {
643 if (typeof this.#parts[0] === 'string') {
644 // this is the string that will match the start of the pattern,
645 // so we need to protect against dots and such.
646 // '.' and '..' cannot match unless the pattern is that exactly,
647 // even if it starts with . or dot:true is set.
648 const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
649 if (!dotTravAllowed) {
650 const aps = addPatternStart;
651 // check if we have a possibility of matching . or ..,
652 // and prevent that.
653 const needNoTrav =
654 // dots are allowed, and the pattern starts with [ or .
655 (dot && aps.has(src.charAt(0))) ||
656 // the pattern starts with \., and then [ or .
657 (src.startsWith('\\.') && aps.has(src.charAt(2))) ||
658 // the pattern starts with \.\., and then [ or .
659 (src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
660 // no need to prevent dots if it can't match a dot, or if a
661 // sub-pattern will be preventing it anyway.
662 const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
663 start =
664 needNoTrav ? startNoTraversal
665 : needNoDot ? startNoDot
666 : '';
667 }
668 }
669 }
670 // append the "end of path portion" pattern to negation tails
671 let end = '';
672 if (this.isEnd() &&
673 this.#root.#filledNegs &&
674 this.#parent?.type === '!') {
675 end = '(?:$|\\/)';
676 }
677 const final = start + src + end;
678 return [
679 final,
680 (0, unescape_js_1.unescape)(src),
681 (this.#hasMagic = !!this.#hasMagic),
682 this.#uflag,
683 ];
684 }
685 // We need to calculate the body *twice* if it's a repeat pattern
686 // at the start, once in nodot mode, then again in dot mode, so a
687 // pattern like *(?) can match 'x.y'
688 const repeated = this.type === '*' || this.type === '+';
689 // some kind of extglob
690 const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
691 let body = this.#partsToRegExp(dot);
692 if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
693 // invalid extglob, has to at least be *something* present, if it's
694 // the entire path portion.
695 const s = this.toString();
[33517cc]696 const me = this;
697 me.#parts = [s];
698 me.type = null;
699 me.#hasMagic = undefined;
[81bc7da]700 return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
701 }
702 let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
703 ''
704 : this.#partsToRegExp(true);
705 if (bodyDotAllowed === body) {
706 bodyDotAllowed = '';
707 }
708 if (bodyDotAllowed) {
709 body = `(?:${body})(?:${bodyDotAllowed})*?`;
710 }
711 // an empty !() is exactly equivalent to a starNoEmpty
712 let final = '';
713 if (this.type === '!' && this.#emptyExt) {
714 final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
715 }
716 else {
717 const close = this.type === '!' ?
718 // !() must match something,but !(x) can match ''
719 '))' +
720 (this.isStart() && !dot && !allowDot ? startNoDot : '') +
721 star +
722 ')'
723 : this.type === '@' ? ')'
724 : this.type === '?' ? ')?'
725 : this.type === '+' && bodyDotAllowed ? ')'
726 : this.type === '*' && bodyDotAllowed ? `)?`
727 : `)${this.type}`;
728 final = start + body + close;
729 }
730 return [
731 final,
732 (0, unescape_js_1.unescape)(body),
733 (this.#hasMagic = !!this.#hasMagic),
734 this.#uflag,
735 ];
736 }
[33517cc]737 #flatten() {
738 if (!isExtglobAST(this)) {
739 for (const p of this.#parts) {
740 if (typeof p === 'object') {
741 p.#flatten();
742 }
743 }
744 }
745 else {
746 // do up to 10 passes to flatten as much as possible
747 let iterations = 0;
748 let done = false;
749 do {
750 done = true;
751 for (let i = 0; i < this.#parts.length; i++) {
752 const c = this.#parts[i];
753 if (typeof c === 'object') {
754 c.#flatten();
755 if (this.#canAdopt(c)) {
756 done = false;
757 this.#adopt(c, i);
758 }
759 else if (this.#canAdoptWithSpace(c)) {
760 done = false;
761 this.#adoptWithSpace(c, i);
762 }
763 else if (this.#canUsurp(c)) {
764 done = false;
765 this.#usurp(c);
766 }
767 }
768 }
769 } while (!done && ++iterations < 10);
770 }
771 this.#toString = undefined;
772 }
[81bc7da]773 #partsToRegExp(dot) {
774 return this.#parts
775 .map(p => {
776 // extglob ASTs should only contain parent ASTs
777 /* c8 ignore start */
778 if (typeof p === 'string') {
779 throw new Error('string type in extglob ast??');
780 }
781 /* c8 ignore stop */
782 // can ignore hasMagic, because extglobs are already always magic
783 const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
784 this.#uflag = this.#uflag || uflag;
785 return re;
786 })
787 .filter(p => !(this.isStart() && this.isEnd()) || !!p)
788 .join('|');
789 }
790 static #parseGlob(glob, hasMagic, noEmpty = false) {
791 let escaping = false;
792 let re = '';
793 let uflag = false;
794 // multiple stars that aren't globstars coalesce into one *
795 let inStar = false;
796 for (let i = 0; i < glob.length; i++) {
797 const c = glob.charAt(i);
798 if (escaping) {
799 escaping = false;
800 re += (reSpecials.has(c) ? '\\' : '') + c;
801 continue;
802 }
803 if (c === '*') {
804 if (inStar)
805 continue;
806 inStar = true;
807 re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
808 hasMagic = true;
809 continue;
810 }
811 else {
812 inStar = false;
813 }
814 if (c === '\\') {
815 if (i === glob.length - 1) {
816 re += '\\\\';
817 }
818 else {
819 escaping = true;
820 }
821 continue;
822 }
823 if (c === '[') {
824 const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i);
825 if (consumed) {
826 re += src;
827 uflag = uflag || needUflag;
828 i += consumed - 1;
829 hasMagic = hasMagic || magic;
830 continue;
831 }
832 }
833 if (c === '?') {
834 re += qmark;
835 hasMagic = true;
836 continue;
837 }
838 re += regExpEscape(c);
839 }
840 return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag];
841 }
842}
843exports.AST = AST;
[33517cc]844_a = AST;
[81bc7da]845//# sourceMappingURL=ast.js.map
Note: See TracBrowser for help on using the repository browser.