Skip to content

Commit

Permalink
fixes #62
Browse files Browse the repository at this point in the history
as well as a few other minor optimizations and linting.
  • Loading branch information
jonschlinkert committed Feb 23, 2020
1 parent 66e1b20 commit 2e56526
Show file tree
Hide file tree
Showing 17 changed files with 244 additions and 102 deletions.
14 changes: 6 additions & 8 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
{
"extends": [
"eslint:recommended"
],
"extends": ["eslint:recommended"],

"env": {
"es6": true,
"node": true
},

"parserOptions":{
"parserOptions": {
"ecmaVersion": 9
},

"rules": {
"accessor-pairs": 2,
"arrow-parens": [2, "as-needed"],
"arrow-spacing": [2, { "before": true, "after": true }],
"block-spacing": [2, "always"],
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
Expand All @@ -26,7 +25,7 @@
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
"handle-callback-err": [2, "^(err|error)$" ],
"handle-callback-err": [2, "^(err|error)$"],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, { "before": true, "after": true }],
Expand All @@ -36,7 +35,6 @@
"no-caller": 2,
"no-class-assign": 2,
"no-cond-assign": 2,
"no-console": 0,
"no-const-assign": 2,
"no-control-regex": 2,
"no-debugger": 2,
Expand Down Expand Up @@ -104,13 +102,13 @@
"one-var": [2, { "initialized": "never" }],
"operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": [0, "never"],
"prefer-const": 2,
"prefer-const": [2, { "destructuring": "all", "ignoreReadBeforeAssign": false }],
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-before-function-paren": [2, { "anonymous": "never", "named": "never", "asyncArrow": "always" }],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
Expand Down
1 change: 1 addition & 0 deletions .verb.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ The following options may be used with the main `picomatch()` function or any of
| `posixSlashes` | `boolean` | `undefined` | Convert all slashes in file paths to forward slashes. This does not convert slashes in the glob pattern itself |
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
| `regex` | `boolean` | `false` | Use regular expression rules for `+` (instead of matching literal `+`), and for stars that follow closing parentheses or brackets (as in `)*` and `]*`). |
| `strictBraces` | `boolean` | `undefined` | Treat brace patterns with no sets or ranges as literals. For example, `{abc}` would be a literal. |
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
| `strictSlashes` | `boolean` | `undefined` | When true, picomatch won't match trailing slashes with single stars. |
| `unescape` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the glob pattern. By default, backslashes are retained. |
Expand Down
10 changes: 5 additions & 5 deletions bench/glob-parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ bench('*.js')
.add(' glob-parent', () => parent('*.js'))
.run();

bench('foo/bar/baz')
.add('picomatch.scan', () => scan('foo/bar/baz'))
.add(' glob-parent', () => parent('foo/bar/baz'))
.run();

bench('foo/*.js')
.add('picomatch.scan', () => scan('foo/*.js'))
.add(' glob-parent', () => parent('foo/*.js'))
Expand All @@ -62,6 +57,11 @@ bench('foo/{a,b}/*.js')
.add(' glob-parent', () => parent('foo/{a,b}/*.js'))
.run();

bench('foo/bar/baz')
.add('picomatch.scan', () => scan('foo/bar/baz'))
.add(' glob-parent', () => parent('foo/bar/baz'))
.run();

bench('*.js { parts: true, tokens: true }')
.add('picomatch.scan', () => scan('*.js', { parts: true, tokens: true }))
.add(' glob-parent', () => parent('*.js'))
Expand Down
33 changes: 19 additions & 14 deletions bench/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,36 @@ const bench = (name, options) => {
};

bench(`${red('.makeRe')} star`)
.add('picomatch', () => pm.makeRe('*'))
.add('picomatch', () => pm.makeRe('*', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('*'))
.run();

bench(`${red('.makeRe')} star; dot=true`)
.add('picomatch', () => pm.makeRe('*', { dot: true }))
bench(`${red('.makeRe')} leading star`)
.add('picomatch', () => pm.makeRe('*.txt', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('*.txt'))
.run();

bench(`${red('.makeRe')} path with star`)
.add('picomatch', () => pm.makeRe('foo/*.js', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('foo/*.js'))
.run();

bench(`${red('.makeRe')} star w/ { dot: true }`)
.add('picomatch', () => pm.makeRe('*', { dot: true , fastpaths: false }))
.add('minimatch', () => mm.makeRe('*', { dot: true }))
.run();

bench(`${red('.makeRe')} globstar`)
.add('picomatch', () => pm.makeRe('**'))
.add('picomatch', () => pm.makeRe('**', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('**'))
.run();

bench(`${red('.makeRe')} globstars`)
.add('picomatch', () => pm.makeRe('**/**/**'))
bench(`${red('.makeRe')} multiple globstars`)
.add('picomatch', () => pm.makeRe('**/**/**', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('**/**/**'))
.run();

bench(`${red('.makeRe')} with leading star`)
.add('picomatch', () => pm.makeRe('*.txt'))
.add('minimatch', () => mm.makeRe('*.txt'))
.run();

bench(`${red('.makeRe')} - basic braces`)
.add('picomatch', () => pm.makeRe('{a,b,c}*.txt'))
.add('minimatch', () => mm.makeRe('{a,b,c}*.txt'))
bench(`${red('.makeRe')} basic braces`)
.add('picomatch', () => pm.makeRe('foo/{a,b,c}*.txt', { fastpaths: false }))
.add('minimatch', () => mm.makeRe('foo/{a,b,c}*.txt'))
.run();
13 changes: 11 additions & 2 deletions bench/load-time.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
'use strict';

const libs = {
get pm() {
return require('..');
},
get mm() {
return require('minimatch');
}
};

console.log('# Load time');
console.time('picomatch');
exports.pm = require('..');
libs.pm;
console.timeEnd('picomatch');
console.time('minimatch');
exports.mm = require('minimatch');
libs.mm;
console.timeEnd('minimatch');
console.log();
59 changes: 59 additions & 0 deletions examples/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,62 @@ console.log(pm.scan('foo/**/*.js'));
console.log(pm.scan('foo/bar/*.js'));
console.log(pm.scan('foo/*.js'));
console.log(pm.scan('/foo'));


const braces = require('braces');

const scan = (pattern, options) => {
const cache = new Map();
const matchers = {};
const patterns = braces.expand(pattern, options);
const result = patterns.map(p => pm.scan(p, options));

for (let i = 0; i < result.length; i++) {
const state = result[i];
if (state.maxDepth === Infinity) continue;
const matcher = matchers[state.base] || (matchers[state.base] = {});
let foundGlob = false;

for (const token of state.tokens) {
if (token.isGlob === true) {
foundGlob = true;
}

if (foundGlob === false) {
continue;
}

if (token.isGlob === false) {
token.matcher = name => token.value === name;
} else {
token.matcher = function glob() {}
}



}
console.log(state);

}

return result;
};

scan('{one/two,foo}/*/abc/{bar,**/*}.js', { parts: true, tokens: true });

// scan('./foo/**/*/*.js', { parts: true, tokens: true });
// scan('**/bar.js', { parts: true, tokens: true });
// scan('foo/**/bar.js', { parts: true, tokens: true });
// scan('foo/**/{bar,*/*}.js', { parts: true, tokens: true });
// scan('foo/**/{bar,*/*}/*.js', { parts: true, tokens: true });
// const { tokens } = scan('foo/*/{bar,*/*}/*.js', { parts: true, tokens: true });
// for (const token of tokens) {
// console.log(token);
// }

// console.log(scan('./foo/**/*/*.js', { parts: true, tokens: true }));
// console.log(scan('foo/**/bar.js', { parts: true, tokens: true }));
// console.log(scan('foo/**/{bar,*/*}.js', { parts: true, tokens: true }));
// console.log(scan('foo/**/{bar,*/*}/*.js', { parts: true, tokens: true }));
// console.log(scan('!./foo/*.js'));
// console.log(scan('!./foo/*.js', { parts: true, tokens: true }));
37 changes: 26 additions & 11 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const parse = (input, options) => {
START_ANCHOR
} = PLATFORM_CHARS;

const globstar = (opts) => {
const globstar = opts => {
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
};

Expand Down Expand Up @@ -260,7 +260,7 @@ const parse = (input, options) => {
* Fast paths
*/

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

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

if (value === '(') {
increment('parens');
push({ type: 'paren', value });
const output = /(?<!\\)\)/.test(remaining()) ? '\\(?(' : '\\(';
push({ type: 'paren', value, output });
continue;
}

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

push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
const rest = remaining();
const special = rest[0] === '?' || rest[0] === '+';
const paren = `)${special ? rest[0] : ''}\\)?`;

if (special) {
consume(rest[0], 1);
}

const output = state.parens ? paren : '\\)';
push({ type: 'paren', value, output });
decrement('parens');
continue;
}
Expand Down Expand Up @@ -579,7 +589,7 @@ const parse = (input, options) => {
state.backtrack = true;
}

if (brace.comma !== true && brace.dots !== true) {
if (opts.strictBraces && brace.comma !== true && brace.dots !== true) {
const out = state.output.slice(0, brace.outputIndex);
const toks = state.tokens.slice(brace.tokensIndex);
brace.value = brace.output = '\\{';
Expand All @@ -604,6 +614,15 @@ const parse = (input, options) => {
if (extglobs.length > 0) {
extglobs[extglobs.length - 1].conditions++;
}

// See: https://github.com/micromatch/picomatch/issues/59
if (opts.strictBraces !== true && braces.length > 0) {
const brace = braces[braces.length - 1];
if (stack[stack.length - 1] === 'braces') {
brace.comma = true;
}
}

push({ type: 'text', value });
continue;
}
Expand Down Expand Up @@ -829,9 +848,7 @@ const parse = (input, options) => {
// strip consecutive `/**/`
while (rest.slice(0, 3) === '/**') {
const after = input[state.index + 4];
if (after && after !== '/') {
break;
}
if (after && after !== '/') break;
rest = rest.slice(3);
consume('/**', 3);
}
Expand Down Expand Up @@ -925,11 +942,9 @@ const parse = (input, options) => {
if (prev.type === 'dot') {
state.output += NO_DOT_SLASH;
prev.output += NO_DOT_SLASH;

} else if (opts.dot === true) {
state.output += NO_DOTS_SLASH;
prev.output += NO_DOTS_SLASH;

} else {
state.output += nodot;
prev.output += nodot;
Expand Down Expand Up @@ -1022,7 +1037,7 @@ parse.fastpaths = (input, options) => {
star = `(${star})`;
}

const globstar = (opts) => {
const globstar = opts => {
if (opts.noglobstar === true) return star;
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
};
Expand Down
Loading

0 comments on commit 2e56526

Please sign in to comment.