Skip to content

Commit 68a3fdf

Browse files
committed
refactor
1 parent bb5b5ba commit 68a3fdf

10 files changed

+520
-380
lines changed

.travis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
os:
2+
- linux
3+
- osx
4+
- windows
15
language: node_js
26
node_js:
37
- node
48
- '10'
59
- '8'
6-
os:
7-
- linux
8-
- osx
9-
- windows

.verb.md

Lines changed: 104 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,66 @@
1+
2+
## v3.0.0 Released!!
3+
4+
See the [changelog](CHANGELOG.md) for details.
5+
16
## Why use braces?
27

3-
Brace patterns are great for matching ranges. Users (and implementors) shouldn't have to think about whether or not they will break their application (or yours) from accidentally defining an aggressive brace pattern. _Braces is the only library that offers a [solution to this problem](#performance)_.
8+
Brace patterns make globs more powerful by adding the ability to match specific ranges and sequences of characters.
9+
10+
- **Accurate** - complete support for the [Bash 4.3 Brace Expansion][bash] specification (passes all of the Bash braces tests)
11+
- **[fast and performant](#benchmarks)** - Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
12+
- **Organized code base** - The parser and compiler are easy to maintain and update when edge cases crop up.
13+
- **Well-tested** - Thousands of test assertions, and passes all of the Bash, minimatch, and [brace-expansion][] unit tests (as of the date this was written).
14+
- **Safer** - You shouldn't have to worry about users defining aggressive or malicious brace patterns that can break your application. Braces takes measures to prevent malicious regex that can be used for DDoS attacks (see [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)).
15+
- [Supports lists](#lists) - (aka "sets") `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
16+
- [Supports sequences](#sequences) - (aka "ranges") `{01..03}` => `['01', '02', '03']`
17+
- [Supports steps](#steps) - (aka "increments") `{2..10..2}` => `['2', '4', '6', '8', '10']`
18+
- [Supports escaping](#escaping) - To prevent evaluation of special characters.
419

5-
- **Safe(r)**: Braces isn't vulnerable to DoS attacks like [brace-expansion][], [minimatch][] and [multimatch][] (a different bug than the [other regex DoS bug][bug]).
6-
- **Accurate**: complete support for the [Bash 4.3 Brace Expansion][bash] specification (passes all of the Bash braces tests)
7-
- **[fast and performant](#benchmarks)**: Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
8-
- **Organized code base**: with parser and compiler that are eas(y|ier) to maintain and update when edge cases crop up.
9-
- **Well-tested**: thousands of test assertions. Passes 100% of the [minimatch][] and [brace-expansion][] unit tests as well (as of the writing of this).
1020

1121
## Usage
1222

1323
The main export is a function that takes one or more brace `patterns` and `options`.
1424

1525
```js
16-
var braces = require('braces');
17-
braces(pattern[, options]);
18-
```
26+
const braces = require('braces');
27+
// braces(patterns[, options]);
1928

20-
By default, braces returns an optimized regex-source string. To get an array of brace patterns, use `brace.expand()`.
29+
console.log(braces(['{01..05}', '{a..e}']));
30+
//=> ['(0[1-5])', '([a-e])']
2131

22-
The following section explains the difference in more detail. _(If you're curious about "why" braces does this by default, see [brace matching pitfalls](#brace-matching-pitfalls)_.
32+
console.log(braces(['{01..05}', '{a..e}'], { expand: true }));
33+
//=> ['01', '02', '03', '04', '05', 'a', 'b', 'c', 'd', 'e']
34+
```
2335

24-
### Optimized vs. expanded braces
36+
### Brace Expansion vs. Compilation
2537

26-
**Optimized**
38+
By default, brace patterns are compiled into strings that are optimized for creating regular expressions and matching.
2739

28-
By default, patterns are optimized for regex and matching:
40+
**Compiled**
2941

3042
```js
31-
console.log(braces('a/{x,y,z}/b'));
43+
console.log(braces('a/{x,y,z}/b'));
3244
//=> ['a/(x|y|z)/b']
45+
console.log(braces(['a/{01..20}/b', 'a/{1..5}/b']));
46+
//=> [ 'a/(0[1-9]|1[0-9]|20)/b', 'a/([1-5])/b' ]
3347
```
3448

3549
**Expanded**
3650

37-
To expand patterns the same way as Bash or [minimatch](https://github.com/isaacs/minimatch), use the [.expand](#expand) method:
51+
Enable brace expansion by setting the `expand` option to true, or by using [braces.expand()](#expand) (returns an array similar to what you'd expect from Bash, or `echo {1..5}`, or [minimatch](https://github.com/isaacs/minimatch)):
3852

3953
```js
40-
console.log(braces.expand('a/{x,y,z}/b'));
54+
console.log(braces('a/{x,y,z}/b', { expand: true }));
4155
//=> ['a/x/b', 'a/y/b', 'a/z/b']
42-
```
43-
44-
Or use [options.expand](#optionsexpand):
4556

46-
```js
47-
console.log(braces('a/{x,y,z}/b', {expand: true}));
48-
//=> ['a/x/b', 'a/y/b', 'a/z/b']
57+
console.log(braces.expand('{01..10}'));
58+
//=> ['01','02','03','04','05','06','07','08','09','10']
4959
```
5060

51-
## Features
52-
53-
* [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
54-
* [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']`
55-
* [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
56-
* [escaping](#escaping)
57-
* [options](#options)
58-
5961
### Lists
6062

61-
Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric lists:
63+
Expand lists (like Bash "sets"):
6264

6365
```js
6466
console.log(braces('a/{foo,bar,baz}/*.js'));
@@ -70,21 +72,23 @@ console.log(braces.expand('a/{foo,bar,baz}/*.js'));
7072

7173
### Sequences
7274

73-
Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
75+
Expand ranges of characters (like Bash "sequences"):
7476

7577
```js
76-
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
77-
console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
78-
console.log(braces.expand('a{1..3}b')); // ['a1b', 'a2b', 'a3b']
79-
console.log(braces.expand('{a..c}')); // ['a', 'b', 'c']
80-
console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
78+
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
79+
console.log(braces.expand('a/{1..3}/b')); // ['a/1/b', 'a/2/b', 'a/3/b']
80+
console.log(braces('{a..c}', { expand: true })); // ['a', 'b', 'c']
81+
console.log(braces('foo/{a..c}', { expand: true })); // ['foo/a', 'foo/b', 'foo/c']
8182

82-
// supports padded ranges
83-
console.log(braces('a{01..03}b')); //=> [ 'a(0[1-3])b' ]
84-
console.log(braces('a{001..300}b')); //=> [ 'a(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)b' ]
83+
// supports zero-padded ranges
84+
console.log(braces('a/{01..03}/b')); //=> ['a/(0[1-3])/b']
85+
console.log(braces('a/{001..300}/b')); //=> ['a/(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)/b']
8586
```
8687

87-
### Steps
88+
See [fill-range](https://github.com/jonschlinkert/fill-range) for all available range-expansion options.
89+
90+
91+
### Steppped ranges
8892

8993
Steps, or increments, may be used with ranges:
9094

@@ -177,43 +181,31 @@ console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
177181

178182
**Default**: `undefined`
179183

180-
**Description**: Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
184+
**Description**: Generate an "expanded" brace pattern (alternatively you can use the `braces.expand()` method, which does the same thing).
181185

182186
```js
183-
console.log(braces('a/{b,c}/d', {expand: true}));
187+
console.log(braces('a/{b,c}/d', { expand: true }));
184188
//=> [ 'a/b/d', 'a/c/d' ]
185189
```
186190

187-
### options.optimize
188-
189-
**Type**: `Boolean`
190-
191-
**Default**: `true`
192-
193-
**Description**: Enabled by default.
194-
195-
```js
196-
console.log(braces('a/{b,c}/d'));
197-
//=> [ 'a/(b|c)/d' ]
198-
```
199-
200191
### options.nodupes
201192

202193
**Type**: `Boolean`
203194

204-
**Default**: `true`
195+
**Default**: `undefined`
196+
197+
**Description**: Remove duplicates from the returned array.
205198

206-
**Description**: Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
207199

208200
### options.rangeLimit
209201

210202
**Type**: `Number`
211203

212-
**Default**: `250`
204+
**Default**: `1000`
213205

214-
**Description**: When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
206+
**Description**: To prevent malicious patterns from being passed by users, an error is thrown when `braces.expand()` is used or `options.expand` is true and the generated range will exceed the `rangeLimit`.
215207

216-
You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
208+
You can customize `options.rangeLimit` or set it to `Inifinity` to disable this altogether.
217209

218210
**Examples**
219211

@@ -235,17 +227,33 @@ console.log(braces.expand('{1..100}'));
235227

236228
**Description**: Customize range expansion.
237229

230+
**Example: Transforming non-numeric values**
231+
238232
```js
239-
var range = braces.expand('x{a..e}y', {
240-
transform: function(str) {
241-
return 'foo' + str;
233+
const alpha = braces.expand('x/{a..e}/y', {
234+
transform(value, index) {
235+
// When non-numeric values are passed, "value" is a character code.
236+
return 'foo/' + String.fromCharCode(value) + '-' + index;
242237
}
243238
});
239+
console.log(alpha);
240+
//=> [ 'x/foo/a-0/y', 'x/foo/b-1/y', 'x/foo/c-2/y', 'x/foo/d-3/y', 'x/foo/e-4/y' ]
241+
```
242+
243+
**Example: Transforming numeric values**
244244

245-
console.log(range);
246-
//=> [ 'xfooay', 'xfooby', 'xfoocy', 'xfoody', 'xfooey' ]
245+
```js
246+
const numeric = braces.expand('{1..5}', {
247+
transform(value) {
248+
// when numeric values are passed, "value" is a number
249+
return 'foo/' + value * 2;
250+
}
251+
});
252+
console.log(numeric);
253+
//=> [ 'foo/2', 'foo/4', 'foo/6', 'foo/8', 'foo/10' ]
247254
```
248255

256+
249257
### options.quantifiers
250258

251259
**Type**: `Boolean`
@@ -258,10 +266,11 @@ Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists]
258266

259267
The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
260268

269+
261270
**Examples**
262271

263272
```js
264-
var braces = require('braces');
273+
const braces = require('braces');
265274
console.log(braces('a/b{1,3}/{x,y,z}'));
266275
//=> [ 'a/b(1|3)/(x|y|z)' ]
267276
console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
@@ -482,83 +491,50 @@ npm i -d && npm benchmark
482491

483492
### Latest results
484493

485-
```bash
486-
Benchmarking: (8 of 8)
487-
· combination-nested
488-
· combination
489-
· escaped
490-
· list-basic
491-
· list-multiple
492-
· no-braces
493-
· sequence-basic
494-
· sequence-multiple
495-
496-
# benchmark/fixtures/combination-nested.js (52 bytes)
497-
brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
498-
braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
499-
minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
500-
501-
fastest is braces
502-
503-
# benchmark/fixtures/combination.js (51 bytes)
504-
brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
505-
braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
506-
minimatch x 637 ops/sec ±0.84% (88 runs sampled)
507-
508-
fastest is braces
494+
Braces is more accurate, without sacrificing performance.
509495

510-
# benchmark/fixtures/escaped.js (44 bytes)
511-
brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
512-
braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
513-
minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
514-
515-
fastest is braces
516-
517-
# benchmark/fixtures/list-basic.js (40 bytes)
518-
brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
519-
braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
520-
minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
521-
522-
fastest is braces
523-
524-
# benchmark/fixtures/list-multiple.js (52 bytes)
525-
brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
526-
braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
527-
minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
496+
```bash
497+
# range (expanded)
498+
braces x 29,040 ops/sec ±3.69% (91 runs sampled))
499+
minimatch x 4,735 ops/sec ±1.28% (90 runs sampled)
528500

529-
fastest is braces
501+
# range (optimized for regex)
502+
braces x 382,878 ops/sec ±0.56% (94 runs sampled)
503+
minimatch x 1,040 ops/sec ±0.44% (93 runs sampled)
530504

531-
# benchmark/fixtures/no-braces.js (48 bytes)
532-
brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
533-
braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
534-
minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
505+
# nested ranges (expanded)
506+
braces x 19,744 ops/sec ±2.27% (92 runs sampled))
507+
minimatch x 4,579 ops/sec ±0.50% (93 runs sampled)
535508

536-
fastest is braces
509+
# nested ranges (optimized for regex)
510+
braces x 246,019 ops/sec ±2.02% (93 runs sampled)
511+
minimatch x 1,028 ops/sec ±0.39% (94 runs sampled)
537512

538-
# benchmark/fixtures/sequence-basic.js (41 bytes)
539-
brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
540-
braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
541-
minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
513+
# set (expanded)
514+
braces x 138,641 ops/sec ±0.53% (95 runs sampled)
515+
minimatch x 219,582 ops/sec ±0.98% (94 runs sampled)
542516

543-
fastest is braces
517+
# set (optimized for regex)
518+
braces x 388,408 ops/sec ±0.41% (95 runs sampled)
519+
minimatch x 44,724 ops/sec ±0.91% (89 runs sampled)
544520

545-
# benchmark/fixtures/sequence-multiple.js (51 bytes)
546-
brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
547-
braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
548-
minimatch x 109 ops/sec ±1.16% (76 runs sampled)
521+
# nested sets (expanded)
522+
braces x 84,966 ops/sec ±0.48% (94 runs sampled)
523+
minimatch x 140,720 ops/sec ±0.37% (95 runs sampled)
549524

550-
fastest is braces
525+
# nested sets (optimized for regex)
526+
braces x 263,340 ops/sec ±2.06% (92 runs sampled)
527+
minimatch x 28,714 ops/sec ±0.40% (90 runs sampled)
551528
```
552529

553530
[^1]: this is the largest safe integer allowed in JavaScript.
554531

555532
[bash]: www.gnu.org/software/bash/
556533
[braces]: https://github.com/jonschlinkert/braces
557534
[brace-expansion]: https://github.com/juliangruber/brace-expansion
558-
[expand-range]: https://github.com/jonschlinkert/expand-range
559535
[fill-range]: https://github.com/jonschlinkert/fill-range
560536
[micromatch]: https://github.com/jonschlinkert/micromatch
561537
[minimatch]: https://github.com/isaacs/minimatch
562538
[quantifiers]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers
563539
[dos]: https://en.wikipedia.org/wiki/Denial-of-service_attack
564-
[bug]: https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc
540+
[bug]: https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc

0 commit comments

Comments
 (0)