Skip to content

Commit 2e56526

Browse files
committed
fixes #62
as well as a few other minor optimizations and linting.
1 parent 66e1b20 commit 2e56526

17 files changed

+244
-102
lines changed

.eslintrc.json

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
{
2-
"extends": [
3-
"eslint:recommended"
4-
],
2+
"extends": ["eslint:recommended"],
53

64
"env": {
75
"es6": true,
86
"node": true
97
},
108

11-
"parserOptions":{
9+
"parserOptions": {
1210
"ecmaVersion": 9
1311
},
1412

1513
"rules": {
1614
"accessor-pairs": 2,
15+
"arrow-parens": [2, "as-needed"],
1716
"arrow-spacing": [2, { "before": true, "after": true }],
1817
"block-spacing": [2, "always"],
1918
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
@@ -26,7 +25,7 @@
2625
"eol-last": 2,
2726
"eqeqeq": [2, "allow-null"],
2827
"generator-star-spacing": [2, { "before": true, "after": true }],
29-
"handle-callback-err": [2, "^(err|error)$" ],
28+
"handle-callback-err": [2, "^(err|error)$"],
3029
"indent": [2, 2, { "SwitchCase": 1 }],
3130
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
3231
"keyword-spacing": [2, { "before": true, "after": true }],
@@ -36,7 +35,6 @@
3635
"no-caller": 2,
3736
"no-class-assign": 2,
3837
"no-cond-assign": 2,
39-
"no-console": 0,
4038
"no-const-assign": 2,
4139
"no-control-regex": 2,
4240
"no-debugger": 2,
@@ -104,13 +102,13 @@
104102
"one-var": [2, { "initialized": "never" }],
105103
"operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
106104
"padded-blocks": [0, "never"],
107-
"prefer-const": 2,
105+
"prefer-const": [2, { "destructuring": "all", "ignoreReadBeforeAssign": false }],
108106
"quotes": [2, "single", "avoid-escape"],
109107
"radix": 2,
110108
"semi": [2, "always"],
111109
"semi-spacing": [2, { "before": false, "after": true }],
112110
"space-before-blocks": [2, "always"],
113-
"space-before-function-paren": [2, "never"],
111+
"space-before-function-paren": [2, { "anonymous": "never", "named": "never", "asyncArrow": "always" }],
114112
"space-in-parens": [2, "never"],
115113
"space-infix-ops": 2,
116114
"space-unary-ops": [2, { "words": true, "nonwords": false }],

.verb.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ The following options may be used with the main `picomatch()` function or any of
125125
| `posixSlashes` | `boolean` | `undefined` | Convert all slashes in file paths to forward slashes. This does not convert slashes in the glob pattern itself |
126126
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
127127
| `regex` | `boolean` | `false` | Use regular expression rules for `+` (instead of matching literal `+`), and for stars that follow closing parentheses or brackets (as in `)*` and `]*`). |
128+
| `strictBraces` | `boolean` | `undefined` | Treat brace patterns with no sets or ranges as literals. For example, `{abc}` would be a literal. |
128129
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
129130
| `strictSlashes` | `boolean` | `undefined` | When true, picomatch won't match trailing slashes with single stars. |
130131
| `unescape` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the glob pattern. By default, backslashes are retained. |

bench/glob-parent.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ bench('*.js')
4747
.add(' glob-parent', () => parent('*.js'))
4848
.run();
4949

50-
bench('foo/bar/baz')
51-
.add('picomatch.scan', () => scan('foo/bar/baz'))
52-
.add(' glob-parent', () => parent('foo/bar/baz'))
53-
.run();
54-
5550
bench('foo/*.js')
5651
.add('picomatch.scan', () => scan('foo/*.js'))
5752
.add(' glob-parent', () => parent('foo/*.js'))
@@ -62,6 +57,11 @@ bench('foo/{a,b}/*.js')
6257
.add(' glob-parent', () => parent('foo/{a,b}/*.js'))
6358
.run();
6459

60+
bench('foo/bar/baz')
61+
.add('picomatch.scan', () => scan('foo/bar/baz'))
62+
.add(' glob-parent', () => parent('foo/bar/baz'))
63+
.run();
64+
6565
bench('*.js { parts: true, tokens: true }')
6666
.add('picomatch.scan', () => scan('*.js', { parts: true, tokens: true }))
6767
.add(' glob-parent', () => parent('*.js'))

bench/index.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,36 @@ const bench = (name, options) => {
4444
};
4545

4646
bench(`${red('.makeRe')} star`)
47-
.add('picomatch', () => pm.makeRe('*'))
47+
.add('picomatch', () => pm.makeRe('*', { fastpaths: false }))
4848
.add('minimatch', () => mm.makeRe('*'))
4949
.run();
5050

51-
bench(`${red('.makeRe')} star; dot=true`)
52-
.add('picomatch', () => pm.makeRe('*', { dot: true }))
51+
bench(`${red('.makeRe')} leading star`)
52+
.add('picomatch', () => pm.makeRe('*.txt', { fastpaths: false }))
53+
.add('minimatch', () => mm.makeRe('*.txt'))
54+
.run();
55+
56+
bench(`${red('.makeRe')} path with star`)
57+
.add('picomatch', () => pm.makeRe('foo/*.js', { fastpaths: false }))
58+
.add('minimatch', () => mm.makeRe('foo/*.js'))
59+
.run();
60+
61+
bench(`${red('.makeRe')} star w/ { dot: true }`)
62+
.add('picomatch', () => pm.makeRe('*', { dot: true , fastpaths: false }))
5363
.add('minimatch', () => mm.makeRe('*', { dot: true }))
5464
.run();
5565

5666
bench(`${red('.makeRe')} globstar`)
57-
.add('picomatch', () => pm.makeRe('**'))
67+
.add('picomatch', () => pm.makeRe('**', { fastpaths: false }))
5868
.add('minimatch', () => mm.makeRe('**'))
5969
.run();
6070

61-
bench(`${red('.makeRe')} globstars`)
62-
.add('picomatch', () => pm.makeRe('**/**/**'))
71+
bench(`${red('.makeRe')} multiple globstars`)
72+
.add('picomatch', () => pm.makeRe('**/**/**', { fastpaths: false }))
6373
.add('minimatch', () => mm.makeRe('**/**/**'))
6474
.run();
6575

66-
bench(`${red('.makeRe')} with leading star`)
67-
.add('picomatch', () => pm.makeRe('*.txt'))
68-
.add('minimatch', () => mm.makeRe('*.txt'))
69-
.run();
70-
71-
bench(`${red('.makeRe')} - basic braces`)
72-
.add('picomatch', () => pm.makeRe('{a,b,c}*.txt'))
73-
.add('minimatch', () => mm.makeRe('{a,b,c}*.txt'))
76+
bench(`${red('.makeRe')} basic braces`)
77+
.add('picomatch', () => pm.makeRe('foo/{a,b,c}*.txt', { fastpaths: false }))
78+
.add('minimatch', () => mm.makeRe('foo/{a,b,c}*.txt'))
7479
.run();

bench/load-time.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
'use strict';
22

3+
const libs = {
4+
get pm() {
5+
return require('..');
6+
},
7+
get mm() {
8+
return require('minimatch');
9+
}
10+
};
11+
312
console.log('# Load time');
413
console.time('picomatch');
5-
exports.pm = require('..');
14+
libs.pm;
615
console.timeEnd('picomatch');
716
console.time('minimatch');
8-
exports.mm = require('minimatch');
17+
libs.mm;
918
console.timeEnd('minimatch');
1019
console.log();

examples/scan.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,62 @@ console.log(pm.scan('foo/**/*.js'));
1515
console.log(pm.scan('foo/bar/*.js'));
1616
console.log(pm.scan('foo/*.js'));
1717
console.log(pm.scan('/foo'));
18+
19+
20+
const braces = require('braces');
21+
22+
const scan = (pattern, options) => {
23+
const cache = new Map();
24+
const matchers = {};
25+
const patterns = braces.expand(pattern, options);
26+
const result = patterns.map(p => pm.scan(p, options));
27+
28+
for (let i = 0; i < result.length; i++) {
29+
const state = result[i];
30+
if (state.maxDepth === Infinity) continue;
31+
const matcher = matchers[state.base] || (matchers[state.base] = {});
32+
let foundGlob = false;
33+
34+
for (const token of state.tokens) {
35+
if (token.isGlob === true) {
36+
foundGlob = true;
37+
}
38+
39+
if (foundGlob === false) {
40+
continue;
41+
}
42+
43+
if (token.isGlob === false) {
44+
token.matcher = name => token.value === name;
45+
} else {
46+
token.matcher = function glob() {}
47+
}
48+
49+
50+
51+
}
52+
console.log(state);
53+
54+
}
55+
56+
return result;
57+
};
58+
59+
scan('{one/two,foo}/*/abc/{bar,**/*}.js', { parts: true, tokens: true });
60+
61+
// scan('./foo/**/*/*.js', { parts: true, tokens: true });
62+
// scan('**/bar.js', { parts: true, tokens: true });
63+
// scan('foo/**/bar.js', { parts: true, tokens: true });
64+
// scan('foo/**/{bar,*/*}.js', { parts: true, tokens: true });
65+
// scan('foo/**/{bar,*/*}/*.js', { parts: true, tokens: true });
66+
// const { tokens } = scan('foo/*/{bar,*/*}/*.js', { parts: true, tokens: true });
67+
// for (const token of tokens) {
68+
// console.log(token);
69+
// }
70+
71+
// console.log(scan('./foo/**/*/*.js', { parts: true, tokens: true }));
72+
// console.log(scan('foo/**/bar.js', { parts: true, tokens: true }));
73+
// console.log(scan('foo/**/{bar,*/*}.js', { parts: true, tokens: true }));
74+
// console.log(scan('foo/**/{bar,*/*}/*.js', { parts: true, tokens: true }));
75+
// console.log(scan('!./foo/*.js'));
76+
// console.log(scan('!./foo/*.js', { parts: true, tokens: true }));

lib/parse.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const parse = (input, options) => {
9292
START_ANCHOR
9393
} = PLATFORM_CHARS;
9494

95-
const globstar = (opts) => {
95+
const globstar = opts => {
9696
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
9797
};
9898

@@ -260,7 +260,7 @@ const parse = (input, options) => {
260260
* Fast paths
261261
*/
262262

263-
if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
263+
if (opts.fastpaths !== false && !/(^[*!]|(?!<\\)[/()[\]{}"])/.test(input)) {
264264
let backslashes = false;
265265

266266
let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
@@ -444,7 +444,8 @@ const parse = (input, options) => {
444444

445445
if (value === '(') {
446446
increment('parens');
447-
push({ type: 'paren', value });
447+
const output = /(?<!\\)\)/.test(remaining()) ? '\\(?(' : '\\(';
448+
push({ type: 'paren', value, output });
448449
continue;
449450
}
450451

@@ -459,7 +460,16 @@ const parse = (input, options) => {
459460
continue;
460461
}
461462

462-
push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
463+
const rest = remaining();
464+
const special = rest[0] === '?' || rest[0] === '+';
465+
const paren = `)${special ? rest[0] : ''}\\)?`;
466+
467+
if (special) {
468+
consume(rest[0], 1);
469+
}
470+
471+
const output = state.parens ? paren : '\\)';
472+
push({ type: 'paren', value, output });
463473
decrement('parens');
464474
continue;
465475
}
@@ -579,7 +589,7 @@ const parse = (input, options) => {
579589
state.backtrack = true;
580590
}
581591

582-
if (brace.comma !== true && brace.dots !== true) {
592+
if (opts.strictBraces && brace.comma !== true && brace.dots !== true) {
583593
const out = state.output.slice(0, brace.outputIndex);
584594
const toks = state.tokens.slice(brace.tokensIndex);
585595
brace.value = brace.output = '\\{';
@@ -604,6 +614,15 @@ const parse = (input, options) => {
604614
if (extglobs.length > 0) {
605615
extglobs[extglobs.length - 1].conditions++;
606616
}
617+
618+
// See: https://github.com/micromatch/picomatch/issues/59
619+
if (opts.strictBraces !== true && braces.length > 0) {
620+
const brace = braces[braces.length - 1];
621+
if (stack[stack.length - 1] === 'braces') {
622+
brace.comma = true;
623+
}
624+
}
625+
607626
push({ type: 'text', value });
608627
continue;
609628
}
@@ -829,9 +848,7 @@ const parse = (input, options) => {
829848
// strip consecutive `/**/`
830849
while (rest.slice(0, 3) === '/**') {
831850
const after = input[state.index + 4];
832-
if (after && after !== '/') {
833-
break;
834-
}
851+
if (after && after !== '/') break;
835852
rest = rest.slice(3);
836853
consume('/**', 3);
837854
}
@@ -925,11 +942,9 @@ const parse = (input, options) => {
925942
if (prev.type === 'dot') {
926943
state.output += NO_DOT_SLASH;
927944
prev.output += NO_DOT_SLASH;
928-
929945
} else if (opts.dot === true) {
930946
state.output += NO_DOTS_SLASH;
931947
prev.output += NO_DOTS_SLASH;
932-
933948
} else {
934949
state.output += nodot;
935950
prev.output += nodot;
@@ -1022,7 +1037,7 @@ parse.fastpaths = (input, options) => {
10221037
star = `(${star})`;
10231038
}
10241039

1025-
const globstar = (opts) => {
1040+
const globstar = opts => {
10261041
if (opts.noglobstar === true) return star;
10271042
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
10281043
};

0 commit comments

Comments
 (0)