blob: 34bdc2f90e5786e3cff20ee702c219977bad4f88 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 10:59:03 +02001'use strict';
2
Yang Guo4fd355c2019-09-19 10:59:03 +02003const constants = require('./constants');
Tim van der Lippe459f4022021-02-19 12:09:57 +00004const utils = require('./utils');
Yang Guo4fd355c2019-09-19 10:59:03 +02005
6/**
7 * Constants
8 */
9
10const {
11 MAX_LENGTH,
12 POSIX_REGEX_SOURCE,
Tim van der Lippe459f4022021-02-19 12:09:57 +000013 REGEX_NON_SPECIAL_CHARS,
Yang Guo4fd355c2019-09-19 10:59:03 +020014 REGEX_SPECIAL_CHARS_BACKREF,
15 REPLACEMENTS
16} = constants;
17
18/**
19 * Helpers
20 */
21
22const expandRange = (args, options) => {
23 if (typeof options.expandRange === 'function') {
24 return options.expandRange(...args, options);
25 }
26
27 args.sort();
Tim van der Lippe459f4022021-02-19 12:09:57 +000028 const value = `[${args.join('-')}]`;
Yang Guo4fd355c2019-09-19 10:59:03 +020029
30 try {
Tim van der Lippe459f4022021-02-19 12:09:57 +000031 /* eslint-disable-next-line no-new */
Yang Guo4fd355c2019-09-19 10:59:03 +020032 new RegExp(value);
33 } catch (ex) {
34 return args.map(v => utils.escapeRegex(v)).join('..');
35 }
36
37 return value;
38};
39
Yang Guo4fd355c2019-09-19 10:59:03 +020040/**
41 * Create the message for a syntax error
42 */
43
44const syntaxError = (type, char) => {
45 return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
46};
47
48/**
49 * Parse the given input string.
50 * @param {String} input
51 * @param {Object} options
52 * @return {Object}
53 */
54
55const parse = (input, options) => {
56 if (typeof input !== 'string') {
57 throw new TypeError('Expected a string');
58 }
59
60 input = REPLACEMENTS[input] || input;
61
Tim van der Lippe459f4022021-02-19 12:09:57 +000062 const opts = { ...options };
63 const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
64
Yang Guo4fd355c2019-09-19 10:59:03 +020065 let len = input.length;
66 if (len > max) {
67 throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
68 }
69
Tim van der Lippe459f4022021-02-19 12:09:57 +000070 const bos = { type: 'bos', value: '', output: opts.prepend || '' };
71 const tokens = [bos];
Yang Guo4fd355c2019-09-19 10:59:03 +020072
Tim van der Lippe459f4022021-02-19 12:09:57 +000073 const capture = opts.capture ? '' : '?:';
74 const win32 = utils.isWindows(options);
Yang Guo4fd355c2019-09-19 10:59:03 +020075
76 // create constants based on platform, for windows or posix
77 const PLATFORM_CHARS = constants.globChars(win32);
78 const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
79
80 const {
81 DOT_LITERAL,
82 PLUS_LITERAL,
83 SLASH_LITERAL,
84 ONE_CHAR,
85 DOTS_SLASH,
86 NO_DOT,
87 NO_DOT_SLASH,
88 NO_DOTS_SLASH,
89 QMARK,
90 QMARK_NO_DOT,
91 STAR,
92 START_ANCHOR
93 } = PLATFORM_CHARS;
94
95 const globstar = (opts) => {
96 return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
97 };
98
Tim van der Lippe459f4022021-02-19 12:09:57 +000099 const nodot = opts.dot ? '' : NO_DOT;
100 const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
Yang Guo4fd355c2019-09-19 10:59:03 +0200101 let star = opts.bash === true ? globstar(opts) : STAR;
Yang Guo4fd355c2019-09-19 10:59:03 +0200102
103 if (opts.capture) {
104 star = `(${star})`;
105 }
106
107 // minimatch options support
108 if (typeof opts.noext === 'boolean') {
109 opts.noextglob = opts.noext;
110 }
111
Tim van der Lippe459f4022021-02-19 12:09:57 +0000112 const state = {
113 input,
Yang Guo4fd355c2019-09-19 10:59:03 +0200114 index: -1,
115 start: 0,
Tim van der Lippe459f4022021-02-19 12:09:57 +0000116 dot: opts.dot === true,
Yang Guo4fd355c2019-09-19 10:59:03 +0200117 consumed: '',
118 output: '',
Tim van der Lippe459f4022021-02-19 12:09:57 +0000119 prefix: '',
Yang Guo4fd355c2019-09-19 10:59:03 +0200120 backtrack: false,
Tim van der Lippe459f4022021-02-19 12:09:57 +0000121 negated: false,
Yang Guo4fd355c2019-09-19 10:59:03 +0200122 brackets: 0,
123 braces: 0,
124 parens: 0,
125 quotes: 0,
Tim van der Lippe459f4022021-02-19 12:09:57 +0000126 globstar: false,
Yang Guo4fd355c2019-09-19 10:59:03 +0200127 tokens
128 };
129
Tim van der Lippe459f4022021-02-19 12:09:57 +0000130 input = utils.removePrefix(input, state);
131 len = input.length;
132
133 const extglobs = [];
134 const braces = [];
135 const stack = [];
Yang Guo4fd355c2019-09-19 10:59:03 +0200136 let prev = bos;
137 let value;
138
139 /**
140 * Tokenizing helpers
141 */
142
143 const eos = () => state.index === len - 1;
144 const peek = state.peek = (n = 1) => input[state.index + n];
145 const advance = state.advance = () => input[++state.index];
Tim van der Lippe459f4022021-02-19 12:09:57 +0000146 const remaining = () => input.slice(state.index + 1);
147 const consume = (value = '', num = 0) => {
148 state.consumed += value;
149 state.index += num;
150 };
Yang Guo4fd355c2019-09-19 10:59:03 +0200151 const append = token => {
152 state.output += token.output != null ? token.output : token.value;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000153 consume(token.value);
154 };
155
156 const negate = () => {
157 let count = 1;
158
159 while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) {
160 advance();
161 state.start++;
162 count++;
163 }
164
165 if (count % 2 === 0) {
166 return false;
167 }
168
169 state.negated = true;
170 state.start++;
171 return true;
Yang Guo4fd355c2019-09-19 10:59:03 +0200172 };
173
174 const increment = type => {
175 state[type]++;
176 stack.push(type);
177 };
178
179 const decrement = type => {
180 state[type]--;
181 stack.pop();
182 };
183
184 /**
185 * Push tokens onto the tokens array. This helper speeds up
186 * tokenizing by 1) helping us avoid backtracking as much as possible,
187 * and 2) helping us avoid creating extra tokens when consecutive
188 * characters are plain text. This improves performance and simplifies
189 * lookbehinds.
190 */
191
192 const push = tok => {
193 if (prev.type === 'globstar') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000194 const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace');
195 const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren'));
196
Yang Guo4fd355c2019-09-19 10:59:03 +0200197 if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) {
198 state.output = state.output.slice(0, -prev.output.length);
199 prev.type = 'star';
200 prev.value = '*';
201 prev.output = star;
202 state.output += prev.output;
203 }
204 }
205
206 if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) {
207 extglobs[extglobs.length - 1].inner += tok.value;
208 }
209
210 if (tok.value || tok.output) append(tok);
211 if (prev && prev.type === 'text' && tok.type === 'text') {
212 prev.value += tok.value;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000213 prev.output = (prev.output || '') + tok.value;
Yang Guo4fd355c2019-09-19 10:59:03 +0200214 return;
215 }
216
217 tok.prev = prev;
218 tokens.push(tok);
219 prev = tok;
220 };
221
222 const extglobOpen = (type, value) => {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000223 const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' };
Yang Guo4fd355c2019-09-19 10:59:03 +0200224
225 token.prev = prev;
226 token.parens = state.parens;
227 token.output = state.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000228 const output = (opts.capture ? '(' : '') + token.open;
Yang Guo4fd355c2019-09-19 10:59:03 +0200229
Tim van der Lippe459f4022021-02-19 12:09:57 +0000230 increment('parens');
Yang Guo4fd355c2019-09-19 10:59:03 +0200231 push({ type, value, output: state.output ? '' : ONE_CHAR });
232 push({ type: 'paren', extglob: true, value: advance(), output });
Yang Guo4fd355c2019-09-19 10:59:03 +0200233 extglobs.push(token);
234 };
235
236 const extglobClose = token => {
237 let output = token.close + (opts.capture ? ')' : '');
238
239 if (token.type === 'negate') {
240 let extglobStar = star;
241
242 if (token.inner && token.inner.length > 1 && token.inner.includes('/')) {
243 extglobStar = globstar(opts);
244 }
245
Tim van der Lippe459f4022021-02-19 12:09:57 +0000246 if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) {
247 output = token.close = `)$))${extglobStar}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200248 }
249
250 if (token.prev.type === 'bos' && eos()) {
251 state.negatedExtglob = true;
252 }
253 }
254
255 push({ type: 'paren', extglob: true, value, output });
256 decrement('parens');
257 };
258
Tim van der Lippe459f4022021-02-19 12:09:57 +0000259 /**
260 * Fast paths
261 */
262
263 if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200264 let backslashes = false;
265
266 let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
267 if (first === '\\') {
268 backslashes = true;
269 return m;
270 }
271
272 if (first === '?') {
273 if (esc) {
274 return esc + first + (rest ? QMARK.repeat(rest.length) : '');
275 }
276 if (index === 0) {
277 return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : '');
278 }
279 return QMARK.repeat(chars.length);
280 }
281
282 if (first === '.') {
283 return DOT_LITERAL.repeat(chars.length);
284 }
285
286 if (first === '*') {
287 if (esc) {
288 return esc + first + (rest ? star : '');
289 }
290 return star;
291 }
Tim van der Lippe459f4022021-02-19 12:09:57 +0000292 return esc ? m : `\\${m}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200293 });
294
295 if (backslashes === true) {
296 if (opts.unescape === true) {
297 output = output.replace(/\\/g, '');
298 } else {
299 output = output.replace(/\\+/g, m => {
300 return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : '');
301 });
302 }
303 }
304
Tim van der Lippe459f4022021-02-19 12:09:57 +0000305 if (output === input && opts.contains === true) {
306 state.output = input;
307 return state;
308 }
309
310 state.output = utils.wrapOutput(output, state, options);
Yang Guo4fd355c2019-09-19 10:59:03 +0200311 return state;
312 }
313
314 /**
315 * Tokenize input until we reach end-of-string
316 */
317
318 while (!eos()) {
319 value = advance();
320
321 if (value === '\u0000') {
322 continue;
323 }
324
325 /**
326 * Escaped characters
327 */
328
329 if (value === '\\') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000330 const next = peek();
Yang Guo4fd355c2019-09-19 10:59:03 +0200331
332 if (next === '/' && opts.bash !== true) {
333 continue;
334 }
335
336 if (next === '.' || next === ';') {
337 continue;
338 }
339
340 if (!next) {
341 value += '\\';
342 push({ type: 'text', value });
343 continue;
344 }
345
346 // collapse slashes to reduce potential for exploits
Tim van der Lippe459f4022021-02-19 12:09:57 +0000347 const match = /^\\+/.exec(remaining());
Yang Guo4fd355c2019-09-19 10:59:03 +0200348 let slashes = 0;
349
350 if (match && match[0].length > 2) {
351 slashes = match[0].length;
352 state.index += slashes;
353 if (slashes % 2 !== 0) {
354 value += '\\';
355 }
356 }
357
358 if (opts.unescape === true) {
359 value = advance() || '';
360 } else {
361 value += advance() || '';
362 }
363
364 if (state.brackets === 0) {
365 push({ type: 'text', value });
366 continue;
367 }
368 }
369
370 /**
371 * If we're inside a regex character class, continue
372 * until we reach the closing bracket.
373 */
374
375 if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) {
376 if (opts.posix !== false && value === ':') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000377 const inner = prev.value.slice(1);
Yang Guo4fd355c2019-09-19 10:59:03 +0200378 if (inner.includes('[')) {
379 prev.posix = true;
380
381 if (inner.includes(':')) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000382 const idx = prev.value.lastIndexOf('[');
383 const pre = prev.value.slice(0, idx);
384 const rest = prev.value.slice(idx + 2);
385 const posix = POSIX_REGEX_SOURCE[rest];
Yang Guo4fd355c2019-09-19 10:59:03 +0200386 if (posix) {
387 prev.value = pre + posix;
388 state.backtrack = true;
389 advance();
390
391 if (!bos.output && tokens.indexOf(prev) === 1) {
392 bos.output = ONE_CHAR;
393 }
394 continue;
395 }
396 }
397 }
398 }
399
400 if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000401 value = `\\${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200402 }
403
404 if (value === ']' && (prev.value === '[' || prev.value === '[^')) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000405 value = `\\${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200406 }
407
408 if (opts.posix === true && value === '!' && prev.value === '[') {
409 value = '^';
410 }
411
412 prev.value += value;
413 append({ value });
414 continue;
415 }
416
417 /**
418 * If we're inside a quoted string, continue
419 * until we reach the closing double quote.
420 */
421
422 if (state.quotes === 1 && value !== '"') {
423 value = utils.escapeRegex(value);
424 prev.value += value;
425 append({ value });
426 continue;
427 }
428
429 /**
430 * Double quotes
431 */
432
433 if (value === '"') {
434 state.quotes = state.quotes === 1 ? 0 : 1;
435 if (opts.keepQuotes === true) {
436 push({ type: 'text', value });
437 }
438 continue;
439 }
440
441 /**
442 * Parentheses
443 */
444
445 if (value === '(') {
Yang Guo4fd355c2019-09-19 10:59:03 +0200446 increment('parens');
Tim van der Lippe459f4022021-02-19 12:09:57 +0000447 push({ type: 'paren', value });
Yang Guo4fd355c2019-09-19 10:59:03 +0200448 continue;
449 }
450
451 if (value === ')') {
452 if (state.parens === 0 && opts.strictBrackets === true) {
453 throw new SyntaxError(syntaxError('opening', '('));
454 }
455
Tim van der Lippe459f4022021-02-19 12:09:57 +0000456 const extglob = extglobs[extglobs.length - 1];
Yang Guo4fd355c2019-09-19 10:59:03 +0200457 if (extglob && state.parens === extglob.parens + 1) {
458 extglobClose(extglobs.pop());
459 continue;
460 }
461
462 push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
463 decrement('parens');
464 continue;
465 }
466
467 /**
Tim van der Lippe459f4022021-02-19 12:09:57 +0000468 * Square brackets
Yang Guo4fd355c2019-09-19 10:59:03 +0200469 */
470
471 if (value === '[') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000472 if (opts.nobracket === true || !remaining().includes(']')) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200473 if (opts.nobracket !== true && opts.strictBrackets === true) {
474 throw new SyntaxError(syntaxError('closing', ']'));
475 }
476
Tim van der Lippe459f4022021-02-19 12:09:57 +0000477 value = `\\${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200478 } else {
479 increment('brackets');
480 }
481
482 push({ type: 'bracket', value });
483 continue;
484 }
485
486 if (value === ']') {
487 if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000488 push({ type: 'text', value, output: `\\${value}` });
Yang Guo4fd355c2019-09-19 10:59:03 +0200489 continue;
490 }
491
492 if (state.brackets === 0) {
493 if (opts.strictBrackets === true) {
494 throw new SyntaxError(syntaxError('opening', '['));
495 }
496
Tim van der Lippe459f4022021-02-19 12:09:57 +0000497 push({ type: 'text', value, output: `\\${value}` });
Yang Guo4fd355c2019-09-19 10:59:03 +0200498 continue;
499 }
500
501 decrement('brackets');
502
Tim van der Lippe459f4022021-02-19 12:09:57 +0000503 const prevValue = prev.value.slice(1);
Yang Guo4fd355c2019-09-19 10:59:03 +0200504 if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000505 value = `/${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200506 }
507
508 prev.value += value;
509 append({ value });
510
511 // when literal brackets are explicitly disabled
512 // assume we should match with a regex character class
513 if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) {
514 continue;
515 }
516
Tim van der Lippe459f4022021-02-19 12:09:57 +0000517 const escaped = utils.escapeRegex(prev.value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200518 state.output = state.output.slice(0, -prev.value.length);
519
520 // when literal brackets are explicitly enabled
521 // assume we should escape the brackets to match literal characters
522 if (opts.literalBrackets === true) {
523 state.output += escaped;
524 prev.value = escaped;
525 continue;
526 }
527
528 // when the user specifies nothing, try to match both
529 prev.value = `(${capture}${escaped}|${prev.value})`;
530 state.output += prev.value;
531 continue;
532 }
533
534 /**
535 * Braces
536 */
537
538 if (value === '{' && opts.nobrace !== true) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200539 increment('braces');
Tim van der Lippe459f4022021-02-19 12:09:57 +0000540
541 const open = {
542 type: 'brace',
543 value,
544 output: '(',
545 outputIndex: state.output.length,
546 tokensIndex: state.tokens.length
547 };
548
549 braces.push(open);
550 push(open);
Yang Guo4fd355c2019-09-19 10:59:03 +0200551 continue;
552 }
553
554 if (value === '}') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000555 const brace = braces[braces.length - 1];
556
557 if (opts.nobrace === true || !brace) {
558 push({ type: 'text', value, output: value });
Yang Guo4fd355c2019-09-19 10:59:03 +0200559 continue;
560 }
561
562 let output = ')';
563
Tim van der Lippe459f4022021-02-19 12:09:57 +0000564 if (brace.dots === true) {
565 const arr = tokens.slice();
566 const range = [];
Yang Guo4fd355c2019-09-19 10:59:03 +0200567
568 for (let i = arr.length - 1; i >= 0; i--) {
569 tokens.pop();
570 if (arr[i].type === 'brace') {
571 break;
572 }
573 if (arr[i].type !== 'dots') {
574 range.unshift(arr[i].value);
575 }
576 }
577
578 output = expandRange(range, opts);
579 state.backtrack = true;
580 }
581
Tim van der Lippe459f4022021-02-19 12:09:57 +0000582 if (brace.comma !== true && brace.dots !== true) {
583 const out = state.output.slice(0, brace.outputIndex);
584 const toks = state.tokens.slice(brace.tokensIndex);
585 brace.value = brace.output = '\\{';
586 value = output = '\\}';
587 state.output = out;
588 for (const t of toks) {
589 state.output += (t.output || t.value);
590 }
591 }
592
Yang Guo4fd355c2019-09-19 10:59:03 +0200593 push({ type: 'brace', value, output });
594 decrement('braces');
Tim van der Lippe459f4022021-02-19 12:09:57 +0000595 braces.pop();
Yang Guo4fd355c2019-09-19 10:59:03 +0200596 continue;
597 }
598
599 /**
600 * Pipes
601 */
602
603 if (value === '|') {
604 if (extglobs.length > 0) {
605 extglobs[extglobs.length - 1].conditions++;
606 }
607 push({ type: 'text', value });
608 continue;
609 }
610
611 /**
612 * Commas
613 */
614
615 if (value === ',') {
616 let output = value;
617
Tim van der Lippe459f4022021-02-19 12:09:57 +0000618 const brace = braces[braces.length - 1];
619 if (brace && stack[stack.length - 1] === 'braces') {
620 brace.comma = true;
Yang Guo4fd355c2019-09-19 10:59:03 +0200621 output = '|';
622 }
623
624 push({ type: 'comma', value, output });
625 continue;
626 }
627
628 /**
629 * Slashes
630 */
631
632 if (value === '/') {
633 // if the beginning of the glob is "./", advance the start
634 // to the current index, and don't add the "./" characters
635 // to the state. This greatly simplifies lookbehinds when
636 // checking for BOS characters like "!" and "." (not "./")
Tim van der Lippe459f4022021-02-19 12:09:57 +0000637 if (prev.type === 'dot' && state.index === state.start + 1) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200638 state.start = state.index + 1;
639 state.consumed = '';
640 state.output = '';
641 tokens.pop();
642 prev = bos; // reset "prev" to the first token
643 continue;
644 }
645
646 push({ type: 'slash', value, output: SLASH_LITERAL });
647 continue;
648 }
649
650 /**
651 * Dots
652 */
653
654 if (value === '.') {
655 if (state.braces > 0 && prev.type === 'dot') {
656 if (prev.value === '.') prev.output = DOT_LITERAL;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000657 const brace = braces[braces.length - 1];
Yang Guo4fd355c2019-09-19 10:59:03 +0200658 prev.type = 'dots';
659 prev.output += value;
660 prev.value += value;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000661 brace.dots = true;
662 continue;
663 }
664
665 if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') {
666 push({ type: 'text', value, output: DOT_LITERAL });
Yang Guo4fd355c2019-09-19 10:59:03 +0200667 continue;
668 }
669
670 push({ type: 'dot', value, output: DOT_LITERAL });
671 continue;
672 }
673
674 /**
675 * Question marks
676 */
677
678 if (value === '?') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000679 const isGroup = prev && prev.value === '(';
680 if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
681 extglobOpen('qmark', value);
682 continue;
683 }
684
Yang Guo4fd355c2019-09-19 10:59:03 +0200685 if (prev && prev.type === 'paren') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000686 const next = peek();
Yang Guo4fd355c2019-09-19 10:59:03 +0200687 let output = value;
688
689 if (next === '<' && !utils.supportsLookbehinds()) {
690 throw new Error('Node.js v10 or higher is required for regex lookbehinds');
691 }
692
Tim van der Lippe459f4022021-02-19 12:09:57 +0000693 if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) {
694 output = `\\${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200695 }
696
697 push({ type: 'text', value, output });
698 continue;
699 }
700
Yang Guo4fd355c2019-09-19 10:59:03 +0200701 if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) {
702 push({ type: 'qmark', value, output: QMARK_NO_DOT });
703 continue;
704 }
705
706 push({ type: 'qmark', value, output: QMARK });
707 continue;
708 }
709
710 /**
711 * Exclamation
712 */
713
714 if (value === '!') {
715 if (opts.noextglob !== true && peek() === '(') {
716 if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) {
717 extglobOpen('negate', value);
718 continue;
719 }
720 }
721
722 if (opts.nonegate !== true && state.index === 0) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000723 negate();
Yang Guo4fd355c2019-09-19 10:59:03 +0200724 continue;
725 }
726 }
727
728 /**
729 * Plus
730 */
731
732 if (value === '+') {
733 if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
734 extglobOpen('plus', value);
735 continue;
736 }
737
Tim van der Lippe459f4022021-02-19 12:09:57 +0000738 if ((prev && prev.value === '(') || opts.regex === false) {
739 push({ type: 'plus', value, output: PLUS_LITERAL });
Yang Guo4fd355c2019-09-19 10:59:03 +0200740 continue;
741 }
742
Tim van der Lippe459f4022021-02-19 12:09:57 +0000743 if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200744 push({ type: 'plus', value });
745 continue;
746 }
747
748 push({ type: 'plus', value: PLUS_LITERAL });
749 continue;
750 }
751
752 /**
753 * Plain text
754 */
755
756 if (value === '@') {
757 if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000758 push({ type: 'at', extglob: true, value, output: '' });
Yang Guo4fd355c2019-09-19 10:59:03 +0200759 continue;
760 }
761
762 push({ type: 'text', value });
763 continue;
764 }
765
766 /**
767 * Plain text
768 */
769
770 if (value !== '*') {
771 if (value === '$' || value === '^') {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000772 value = `\\${value}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200773 }
774
Tim van der Lippe459f4022021-02-19 12:09:57 +0000775 const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
Yang Guo4fd355c2019-09-19 10:59:03 +0200776 if (match) {
777 value += match[0];
778 state.index += match[0].length;
779 }
780
781 push({ type: 'text', value });
782 continue;
783 }
784
785 /**
786 * Stars
787 */
788
789 if (prev && (prev.type === 'globstar' || prev.star === true)) {
790 prev.type = 'star';
791 prev.star = true;
792 prev.value += value;
793 prev.output = star;
794 state.backtrack = true;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000795 state.globstar = true;
796 consume(value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200797 continue;
798 }
799
Tim van der Lippe459f4022021-02-19 12:09:57 +0000800 let rest = remaining();
801 if (opts.noextglob !== true && /^\([^?]/.test(rest)) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200802 extglobOpen('star', value);
803 continue;
804 }
805
806 if (prev.type === 'star') {
807 if (opts.noglobstar === true) {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000808 consume(value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200809 continue;
810 }
811
Tim van der Lippe459f4022021-02-19 12:09:57 +0000812 const prior = prev.prev;
813 const before = prior.prev;
814 const isStart = prior.type === 'slash' || prior.type === 'bos';
815 const afterStar = before && (before.type === 'star' || before.type === 'globstar');
Yang Guo4fd355c2019-09-19 10:59:03 +0200816
Tim van der Lippe459f4022021-02-19 12:09:57 +0000817 if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200818 push({ type: 'star', value, output: '' });
819 continue;
820 }
821
Tim van der Lippe459f4022021-02-19 12:09:57 +0000822 const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace');
823 const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren');
Yang Guo4fd355c2019-09-19 10:59:03 +0200824 if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) {
825 push({ type: 'star', value, output: '' });
826 continue;
827 }
828
829 // strip consecutive `/**/`
Tim van der Lippe459f4022021-02-19 12:09:57 +0000830 while (rest.slice(0, 3) === '/**') {
831 const after = input[state.index + 4];
Yang Guo4fd355c2019-09-19 10:59:03 +0200832 if (after && after !== '/') {
833 break;
834 }
Tim van der Lippe459f4022021-02-19 12:09:57 +0000835 rest = rest.slice(3);
836 consume('/**', 3);
Yang Guo4fd355c2019-09-19 10:59:03 +0200837 }
838
839 if (prior.type === 'bos' && eos()) {
840 prev.type = 'globstar';
841 prev.value += value;
842 prev.output = globstar(opts);
843 state.output = prev.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000844 state.globstar = true;
845 consume(value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200846 continue;
847 }
848
849 if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) {
850 state.output = state.output.slice(0, -(prior.output + prev.output).length);
Tim van der Lippe459f4022021-02-19 12:09:57 +0000851 prior.output = `(?:${prior.output}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200852
853 prev.type = 'globstar';
Tim van der Lippe459f4022021-02-19 12:09:57 +0000854 prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)');
Yang Guo4fd355c2019-09-19 10:59:03 +0200855 prev.value += value;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000856 state.globstar = true;
Yang Guo4fd355c2019-09-19 10:59:03 +0200857 state.output += prior.output + prev.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000858 consume(value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200859 continue;
860 }
861
Tim van der Lippe459f4022021-02-19 12:09:57 +0000862 if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') {
863 const end = rest[1] !== void 0 ? '|$' : '';
Yang Guo4fd355c2019-09-19 10:59:03 +0200864
865 state.output = state.output.slice(0, -(prior.output + prev.output).length);
Tim van der Lippe459f4022021-02-19 12:09:57 +0000866 prior.output = `(?:${prior.output}`;
Yang Guo4fd355c2019-09-19 10:59:03 +0200867
868 prev.type = 'globstar';
869 prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
870 prev.value += value;
871
872 state.output += prior.output + prev.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000873 state.globstar = true;
Yang Guo4fd355c2019-09-19 10:59:03 +0200874
Tim van der Lippe459f4022021-02-19 12:09:57 +0000875 consume(value + advance());
876
877 push({ type: 'slash', value: '/', output: '' });
Yang Guo4fd355c2019-09-19 10:59:03 +0200878 continue;
879 }
880
Tim van der Lippe459f4022021-02-19 12:09:57 +0000881 if (prior.type === 'bos' && rest[0] === '/') {
Yang Guo4fd355c2019-09-19 10:59:03 +0200882 prev.type = 'globstar';
883 prev.value += value;
884 prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
885 state.output = prev.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000886 state.globstar = true;
887 consume(value + advance());
888 push({ type: 'slash', value: '/', output: '' });
Yang Guo4fd355c2019-09-19 10:59:03 +0200889 continue;
890 }
891
892 // remove single star from output
893 state.output = state.output.slice(0, -prev.output.length);
894
895 // reset previous token to globstar
896 prev.type = 'globstar';
897 prev.output = globstar(opts);
898 prev.value += value;
899
900 // reset output with globstar
901 state.output += prev.output;
Tim van der Lippe459f4022021-02-19 12:09:57 +0000902 state.globstar = true;
903 consume(value);
Yang Guo4fd355c2019-09-19 10:59:03 +0200904 continue;
905 }
906
Tim van der Lippe459f4022021-02-19 12:09:57 +0000907 const token = { type: 'star', value, output: star };
Yang Guo4fd355c2019-09-19 10:59:03 +0200908
909 if (opts.bash === true) {
910 token.output = '.*?';
911 if (prev.type === 'bos' || prev.type === 'slash') {
912 token.output = nodot + token.output;
913 }
914 push(token);
915 continue;
916 }
917
918 if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) {
919 token.output = value;
920 push(token);
921 continue;
922 }
923
924 if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') {
925 if (prev.type === 'dot') {
926 state.output += NO_DOT_SLASH;
927 prev.output += NO_DOT_SLASH;
928
929 } else if (opts.dot === true) {
930 state.output += NO_DOTS_SLASH;
931 prev.output += NO_DOTS_SLASH;
932
933 } else {
934 state.output += nodot;
935 prev.output += nodot;
936 }
937
938 if (peek() !== '*') {
939 state.output += ONE_CHAR;
940 prev.output += ONE_CHAR;
941 }
942 }
943
944 push(token);
945 }
946
947 while (state.brackets > 0) {
948 if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']'));
949 state.output = utils.escapeLast(state.output, '[');
950 decrement('brackets');
951 }
952
953 while (state.parens > 0) {
954 if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')'));
955 state.output = utils.escapeLast(state.output, '(');
956 decrement('parens');
957 }
958
959 while (state.braces > 0) {
960 if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}'));
961 state.output = utils.escapeLast(state.output, '{');
962 decrement('braces');
963 }
964
965 if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) {
966 push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` });
967 }
968
969 // rebuild the output if we had to backtrack at any point
970 if (state.backtrack === true) {
971 state.output = '';
972
Tim van der Lippe459f4022021-02-19 12:09:57 +0000973 for (const token of state.tokens) {
Yang Guo4fd355c2019-09-19 10:59:03 +0200974 state.output += token.output != null ? token.output : token.value;
975
976 if (token.suffix) {
977 state.output += token.suffix;
978 }
979 }
980 }
981
982 return state;
983};
984
985/**
986 * Fast paths for creating regular expressions for common glob patterns.
987 * This can significantly speed up processing and has very little downside
988 * impact when none of the fast paths match.
989 */
990
991parse.fastpaths = (input, options) => {
Tim van der Lippe459f4022021-02-19 12:09:57 +0000992 const opts = { ...options };
993 const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
994 const len = input.length;
Yang Guo4fd355c2019-09-19 10:59:03 +0200995 if (len > max) {
996 throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
997 }
998
999 input = REPLACEMENTS[input] || input;
Tim van der Lippe459f4022021-02-19 12:09:57 +00001000 const win32 = utils.isWindows(options);
Yang Guo4fd355c2019-09-19 10:59:03 +02001001
1002 // create constants based on platform, for windows or posix
1003 const {
1004 DOT_LITERAL,
1005 SLASH_LITERAL,
1006 ONE_CHAR,
1007 DOTS_SLASH,
1008 NO_DOT,
1009 NO_DOTS,
1010 NO_DOTS_SLASH,
1011 STAR,
1012 START_ANCHOR
1013 } = constants.globChars(win32);
1014
Tim van der Lippe459f4022021-02-19 12:09:57 +00001015 const nodot = opts.dot ? NO_DOTS : NO_DOT;
1016 const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
1017 const capture = opts.capture ? '' : '?:';
1018 const state = { negated: false, prefix: '' };
Yang Guo4fd355c2019-09-19 10:59:03 +02001019 let star = opts.bash === true ? '.*?' : STAR;
Yang Guo4fd355c2019-09-19 10:59:03 +02001020
1021 if (opts.capture) {
1022 star = `(${star})`;
1023 }
1024
1025 const globstar = (opts) => {
Tim van der Lippe459f4022021-02-19 12:09:57 +00001026 if (opts.noglobstar === true) return star;
Yang Guo4fd355c2019-09-19 10:59:03 +02001027 return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
1028 };
1029
1030 const create = str => {
1031 switch (str) {
1032 case '*':
1033 return `${nodot}${ONE_CHAR}${star}`;
1034
1035 case '.*':
1036 return `${DOT_LITERAL}${ONE_CHAR}${star}`;
1037
1038 case '*.*':
1039 return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
1040
1041 case '*/*':
1042 return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
1043
1044 case '**':
1045 return nodot + globstar(opts);
1046
1047 case '**/*':
1048 return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
1049
1050 case '**/*.*':
1051 return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
1052
1053 case '**/.*':
1054 return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
1055
1056 default: {
Tim van der Lippe459f4022021-02-19 12:09:57 +00001057 const match = /^(.*?)\.(\w+)$/.exec(str);
Yang Guo4fd355c2019-09-19 10:59:03 +02001058 if (!match) return;
1059
Tim van der Lippe459f4022021-02-19 12:09:57 +00001060 const source = create(match[1]);
Yang Guo4fd355c2019-09-19 10:59:03 +02001061 if (!source) return;
1062
1063 return source + DOT_LITERAL + match[2];
1064 }
1065 }
1066 };
1067
Tim van der Lippe459f4022021-02-19 12:09:57 +00001068 const output = utils.removePrefix(input, state);
1069 let source = create(output);
1070
1071 if (source && opts.strictSlashes !== true) {
1072 source += `${SLASH_LITERAL}?`;
Yang Guo4fd355c2019-09-19 10:59:03 +02001073 }
1074
Tim van der Lippe459f4022021-02-19 12:09:57 +00001075 return source;
Yang Guo4fd355c2019-09-19 10:59:03 +02001076};
1077
1078module.exports = parse;