diff --git a/.travis.yml b/.travis.yml index 1686664..693c6db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,9 @@ -sudo: false -os: - - linux - - osx language: node_js node_js: - node - - '9' + - '10' - '8' - - '7' - - '6' - - '5' - - '4' - - '0.12' - - '0.10' +os: + - linux + - osx + - windows diff --git a/README.md b/README.md index f909bfb..b45d311 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces) [![Windows Build Status](https://img.shields.io/appveyor/ci/micromatch/braces.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/micromatch/braces) +# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces) > Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed. @@ -9,7 +9,7 @@ Please consider following this project's author, [Jon Schlinkert](https://github Install with [npm](https://www.npmjs.com/): ```sh -$ npm install --save braces +$ npm install braces ``` ## Why use braces? @@ -637,4 +637,4 @@ _This file was generated by [verb-generate-readme](https://github.com/verbose/ve - \ No newline at end of file + diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index be354d3..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Test against this version of Node.js -environment: - matrix: - # node.js - - nodejs_version: "7.0" - - nodejs_version: "6.0" - - nodejs_version: "5.0" - - nodejs_version: "0.12" - - nodejs_version: "0.10" - -# Install scripts. (runs after repo cloning) -install: - # Get the latest stable version of Node.js or io.js - - ps: Install-Product node $env:nodejs_version - # install modules - - npm install - -# Post-install test scripts. -test_script: - # Output useful info for debugging. - - node --version - - npm --version - # run tests - - npm test - -# Don't actually build. -build: off diff --git a/bench/index.js b/bench/index.js new file mode 100644 index 0000000..e4b47bf --- /dev/null +++ b/bench/index.js @@ -0,0 +1,71 @@ +'use strict'; + +const { Suite } = require('benchmark'); +const colors = require('ansi-colors'); +const argv = require('minimist')(process.argv.slice(2)); +const minimatch = require('minimatch'); +const braces = require('..'); + +/** + * Setup + */ + +const cycle = (e, newline) => { + process.stdout.write(`\u001b[G ${e.target}${newline ? `\n` : ''}`); +}; + +const bench = (name, options) => { + const config = { name, ...options }; + const suite = new Suite(config); + const add = suite.add.bind(suite); + suite.on('error', console.error); + + if (argv.run && !new RegExp(argv.run).test(name)) { + suite.add = () => suite; + return suite; + } + + console.log(colors.green(`● ${config.name}`)); + + suite.add = (key, fn, opts) => { + if (typeof fn !== 'function') opts = fn; + + add(key, { + onCycle: e => cycle(e), + onComplete: e => cycle(e, true), + fn, + ...opts + }); + return suite; + }; + + return suite; +}; + +const skip = () => {}; +skip.add = () => skip; +skip.run = () => skip; +bench.skip = name => { + console.log(colors.cyan('● ' + colors.unstyle(name) + ' (skipped)')); + return skip; +}; + +bench('expand - set') + .add(' braces', () => braces.compile('foo/{a,b,c}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar')) + .run(); + +bench('expand - range') + .add(' braces', () => braces.compile('foo/{a..z}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar')) + .run(); + +bench('expand - nested sets') + .add(' braces', () => braces.compile('foo/{a,b,{x,y,z}}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar')) + .run(); + +bench('expand - nested ranges') + .add(' braces', () => braces.compile('foo/{a,b,{1..25}}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,{1..25}}/bar')) + .run(); diff --git a/bench/package.json b/bench/package.json new file mode 100644 index 0000000..28aaf2b --- /dev/null +++ b/bench/package.json @@ -0,0 +1,21 @@ +{ + "name": "picomatch-benchmarks", + "version": "0.0.0", + "private": true, + "main": "index.js", + "dependencies": { + "ansi-colors": "^3.0.3", + "benchmark": "^2.1.4", + "minimatch": "^3.0.4", + "minimist": "^1.2.0" + }, + "lintDeps": { + "devDependencies": { + "files": { + "patterns": [ + "*.js" + ] + } + } + } +} diff --git a/benchmark/code/brace-expansion.js b/benchmark/code/brace-expansion.js deleted file mode 100644 index 8f1599b..0000000 --- a/benchmark/code/brace-expansion.js +++ /dev/null @@ -1,4 +0,0 @@ -var braceExpansion = require('brace-expansion'); -module.exports = function(args) { - return braceExpansion.apply(null, Array.isArray(args) ? args : [args]); -}; diff --git a/benchmark/code/braces.js b/benchmark/code/braces.js deleted file mode 100644 index 2e93852..0000000 --- a/benchmark/code/braces.js +++ /dev/null @@ -1,4 +0,0 @@ -var braces = require('../..'); -module.exports = function(args) { - return braces.apply(null, Array.isArray(args) ? args : [args]); -}; diff --git a/benchmark/code/minimatch.js b/benchmark/code/minimatch.js deleted file mode 100644 index 5eea207..0000000 --- a/benchmark/code/minimatch.js +++ /dev/null @@ -1,4 +0,0 @@ -var braceExpand = require('minimatch').braceExpand; -module.exports = function(args) { - return braceExpand.apply(null, Array.isArray(args) ? args : [args]); -}; diff --git a/benchmark/fixtures/combination-nested.js b/benchmark/fixtures/combination-nested.js deleted file mode 100644 index bed3e8d..0000000 --- a/benchmark/fixtures/combination-nested.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a{b,c{1..100}/{foo/bar},h}x/z']; diff --git a/benchmark/fixtures/combination.js b/benchmark/fixtures/combination.js deleted file mode 100644 index 699795a..0000000 --- a/benchmark/fixtures/combination.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/{b,c,d}/{e,f,g}/h/{1..100}']; diff --git a/benchmark/fixtures/escaped.js b/benchmark/fixtures/escaped.js deleted file mode 100644 index b5e69d5..0000000 --- a/benchmark/fixtures/escaped.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/\\{b,c}/{x\\,y}/d/e']; diff --git a/benchmark/fixtures/list-basic.js b/benchmark/fixtures/list-basic.js deleted file mode 100644 index 916273a..0000000 --- a/benchmark/fixtures/list-basic.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/{x,y,z}/d/e']; diff --git a/benchmark/fixtures/list-multiple.js b/benchmark/fixtures/list-multiple.js deleted file mode 100644 index 405aac2..0000000 --- a/benchmark/fixtures/list-multiple.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/{k,l,m}/d/{w,x,y,z}/d/e']; diff --git a/benchmark/fixtures/match.multiple.js b/benchmark/fixtures/match.multiple.js deleted file mode 100644 index 699795a..0000000 --- a/benchmark/fixtures/match.multiple.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/{b,c,d}/{e,f,g}/h/{1..100}']; diff --git a/benchmark/fixtures/match.sequence.js b/benchmark/fixtures/match.sequence.js deleted file mode 100644 index e94a78f..0000000 --- a/benchmark/fixtures/match.sequence.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/{1..100}/d/e']; diff --git a/benchmark/fixtures/no-braces.js b/benchmark/fixtures/no-braces.js deleted file mode 100644 index 3b4653e..0000000 --- a/benchmark/fixtures/no-braces.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/d/e/**/@(x|y|z)*.js']; diff --git a/benchmark/fixtures/sequence-basic.js b/benchmark/fixtures/sequence-basic.js deleted file mode 100644 index e94a78f..0000000 --- a/benchmark/fixtures/sequence-basic.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/{1..100}/d/e']; diff --git a/benchmark/fixtures/sequence-multiple.js b/benchmark/fixtures/sequence-multiple.js deleted file mode 100644 index ba55e69..0000000 --- a/benchmark/fixtures/sequence-multiple.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a/b/c/{1..50}/d/{1..100}/d/e']; diff --git a/benchmark/index.js b/benchmark/index.js deleted file mode 100644 index 23b4d80..0000000 --- a/benchmark/index.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var path = require('path'); -var util = require('util'); -var cyan = require('ansi-cyan'); -var argv = require('yargs-parser')(process.argv.slice(2)); -var Suite = require('benchmarked'); - -function run(type, fixtures) { - var suite = new Suite({ - cwd: __dirname, - fixtures: `fixtures/${fixtures}.js`, - code: `code/${type}.js` - }); - - if (argv.dry) { - suite.dryRun(function(code, fixture) { - console.log(cyan('%s > %s'), code.key, fixture.key); - var args = require(fixture.path); - var res = code.run(args); - console.log(util.inspect(res, null, 10)); - console.log(); - }); - } else { - suite.run(); - } -} - -run(argv.code || '*', argv._[0] || '!(match)*'); -// run('braces', 'no-*'); diff --git a/benchmark/last.md b/benchmark/last.md deleted file mode 100644 index e805f89..0000000 --- a/benchmark/last.md +++ /dev/null @@ -1,65 +0,0 @@ -Benchmarking: (8 of 8) - · combination-nested - · combination - · escaped - · list-basic - · list-multiple - · no-braces - · sequence-basic - · sequence-multiple - -# benchmark/fixtures/combination-nested.js (52 bytes) - brace-expansion x 5,605 ops/sec ±1.14% (83 runs sampled) - braces x 14,410,490 ops/sec ±1.15% (85 runs sampled) - minimatch x 5,977 ops/sec ±1.28% (85 runs sampled) - - fastest is braces - -# benchmark/fixtures/combination.js (51 bytes) - brace-expansion x 755 ops/sec ±1.18% (83 runs sampled) - braces x 10,759,364 ops/sec ±0.94% (85 runs sampled) - minimatch x 723 ops/sec ±0.98% (84 runs sampled) - - fastest is braces - -# benchmark/fixtures/escaped.js (44 bytes) - brace-expansion x 189,901 ops/sec ±1.23% (86 runs sampled) - braces x 10,832,036 ops/sec ±0.89% (85 runs sampled) - minimatch x 150,475 ops/sec ±1.29% (88 runs sampled) - - fastest is braces - -# benchmark/fixtures/list-basic.js (40 bytes) - brace-expansion x 126,961 ops/sec ±0.70% (85 runs sampled) - braces x 11,004,254 ops/sec ±1.29% (84 runs sampled) - minimatch x 111,199 ops/sec ±1.26% (85 runs sampled) - - fastest is braces - -# benchmark/fixtures/list-multiple.js (52 bytes) - brace-expansion x 36,894 ops/sec ±0.70% (86 runs sampled) - braces x 8,609,924 ops/sec ±1.03% (85 runs sampled) - minimatch x 41,010 ops/sec ±1.17% (88 runs sampled) - - fastest is braces - -# benchmark/fixtures/no-braces.js (48 bytes) - brace-expansion x 309,785 ops/sec ±0.82% (88 runs sampled) - braces x 8,709,136 ops/sec ±1.23% (88 runs sampled) - minimatch x 2,208,995 ops/sec ±1.03% (88 runs sampled) - - fastest is braces - -# benchmark/fixtures/sequence-basic.js (41 bytes) - brace-expansion x 6,236 ops/sec ±0.94% (83 runs sampled) - braces x 9,241,779 ops/sec ±1.26% (83 runs sampled) - minimatch x 7,230 ops/sec ±1.35% (85 runs sampled) - - fastest is braces - -# benchmark/fixtures/sequence-multiple.js (51 bytes) - brace-expansion x 133 ops/sec ±1.08% (73 runs sampled) - braces x 8,859,756 ops/sec ±1.31% (85 runs sampled) - minimatch x 135 ops/sec ±0.94% (73 runs sampled) - - fastest is braces diff --git a/examples/ast.js b/examples/ast.js deleted file mode 100644 index bfecbef..0000000 --- a/examples/ast.js +++ /dev/null @@ -1,20 +0,0 @@ -var braces = require('..'); - -var ast = braces.parse('a/{\\{b,c,d},z}/e', {unescape: false}); -var str = ''; -console.log(ast); - -visit(ast, function(node) { - if (node.val) str += node.val; -}); - -function visit(node, fn) { - return node.nodes ? mapVisit(node, fn) : fn(node); -} - -function mapVisit(node, fn) { - for (var i = 0; i < node.nodes.length; i++) { - visit(node.nodes[i], fn); - } - return node; -} diff --git a/examples/braces.js b/examples/braces.js deleted file mode 100644 index 9692522..0000000 --- a/examples/braces.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var braces = require('..'); -// console.log(braces('some/path/{a,b,c}')); -// console.log(braces.expand('some/path/{a,b,c}')); - -var res = braces('{1..10000000}'); -console.log(res); -// console.log(format(res[0].length)); - -var res = braces('{1..100000000}'); -console.log(res); -// console.log(format(res[0].length)); diff --git a/examples/brackets.js b/examples/brackets.js deleted file mode 100644 index d37600b..0000000 --- a/examples/brackets.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var braces = require('..'); -var list = ['foo.js', '(a).js', '(b).js', '(c).js']; -console.log(braces.match(list, '{([a-b]),foo}.js')); -console.log(braces.expand('{([a-b]),foo}.js')); -console.log(braces.expand('{[dec-1992],[dec-1993]}.js')); diff --git a/examples/comparison.js b/examples/comparison.js deleted file mode 100644 index c3d1819..0000000 --- a/examples/comparison.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var braceExpansion = require('brace-expansion'); -var braces = require('..'); - -console.log('braces'); -console.log(braces('http://any.org/archive{1996..1999}/vol({1..4})/part{a,b,c}.html')); -console.log(braces('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html')); -console.log(braces('http://www.numericals.com/file{1..100..10}.txt')); -console.log(braces('http://www.letters.com/file{a..z..2}.txt')); -console.log(braces('mkdir /usr/local/src/bash/{old,new,dist,bugs}')); -console.log(braces('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}')); -console.log(); -console.log(); -console.log('braces: {expand: true}'); -console.log(braces('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html', {expand: true})); -console.log(braces('http://www.numericals.com/file{1..100..10}.txt', {expand: true})); -console.log(braces('http://www.letters.com/file{a..z..2}.txt', {expand: true})); -console.log(braces('mkdir /usr/local/src/bash/{old,new,dist,bugs}', {expand: true})); -console.log(braces('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}', {expand: true})); -console.log(); -console.log(); -console.log('brace-expansion'); -console.log(braceExpansion('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html')); -console.log(braceExpansion('http://www.numericals.com/file{1..100..10}.txt')); -console.log(braceExpansion('http://www.letters.com/file{a..z..2}.txt')); -console.log(braceExpansion('mkdir /usr/local/src/bash/{old,new,dist,bugs}')); -console.log(braceExpansion('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}')); - - -console.log(braces('user-{200..300}/project-{a,b,c}-{1..10}')) -//=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' -console.log(braces.makeRe('user-{200..300}')) -//=> /^(?:user-(20[0-9]|2[1-9][0-9]|300))$/ diff --git a/examples/compile.js b/examples/compile.js new file mode 100644 index 0000000..710f221 --- /dev/null +++ b/examples/compile.js @@ -0,0 +1,6 @@ +'use strict'; + +const compile = require('../lib/compile'); +const parse = require('../lib/parse'); +console.log(compile(parse('{a,b,c}'))); +console.log(compile(parse('{01..09}'))); diff --git a/examples/escape.js b/examples/escape.js deleted file mode 100644 index c597ec2..0000000 --- a/examples/escape.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -var braces = require('..'); -console.log(braces.expand('{1\\.2}')); diff --git a/examples/expand.js b/examples/expand.js index 2acf235..8efaefc 100644 --- a/examples/expand.js +++ b/examples/expand.js @@ -1,14 +1,24 @@ -'use strict'; -var mm = require('minimatch'); -var braces = require('..'); +const colors = require('ansi-colors'); +const parse = require('./parse'); +const color = (arr, c) => arr.map(s => c(s)).join(', '); +const cp = require('child_process'); +const braces = input => { + return cp.execSync(`echo ${input}`).toString().trim().split(' '); +}; -console.log(braces.expand('a/{b,c}/d')); -//=> [ 'a/b/d', 'a/c/d' ] - -var ast = braces.parse('a{b{a,b}}c'); -var res = braces.compile(ast, {expand: true}); -console.log(res) - -console.log(braces.expand('{1..100}{1..100}').length); -console.log(braces.expand('{1..10}{1..10}{1..10}').length); +// const fixture = '{a,{b,c},d}'; +// const fixture = '{a,b}{c,d}{e,f}'; +// const fixture = 'a/{b,c{x,y}d,e}/f'; +// const fixture = '{{a,b}/i,j,k}'; +// const fixture = '{c,d{e,f}g,h}'; +// const fixture = '{{c,d{e,f}g,h}/i,j,k}'; +// const fixture = '{a,b}/{c,d{e,f}g,h}'; +// const fixture = '{{a,b}/{c,d{e,f}g,h}/i,j,k}'; +// const fixture = '{x,y,{a,b,c\\}}'; +const fixture = 'a{,b}c'; +console.log(); +console.log(' FIXTURE:', colors.magenta(fixture)); +console.log(' ACTUAL:', color(expand(parse(fixture)), colors.yellow)); +console.log('EXPECTED:', color(braces(fixture), colors.blue)); +console.log(); diff --git a/examples/extglobs.js b/examples/extglobs.js deleted file mode 100644 index 800889c..0000000 --- a/examples/extglobs.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -var braces = require('..'); -console.log(braces.expand('{!(a|b),!(c|d)}')); -console.log(braces('{!(a|b),!(c|d)}')); diff --git a/examples/nested-regex.js b/examples/nested-regex.js deleted file mode 100644 index 6d6d532..0000000 --- a/examples/nested-regex.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -var minimatch = require('minimatch'); -var braces = require('..'); - -console.log('braces'); -console.log(braces.makeRe('a{b,c{1..100}/{foo/bar},h}x/z')); -console.log(); -console.log(); -console.log('brace-expansion'); -console.log(minimatch.makeRe('a{b,c{1..100}/{foo/bar},h}x/z')); - diff --git a/examples/nested.js b/examples/nested.js deleted file mode 100644 index 3677928..0000000 --- a/examples/nested.js +++ /dev/null @@ -1,49 +0,0 @@ -var util = require('util'); -var braces = require('..').expand; -var expand = require('brace-expansion'); -var bash = require('../test/support/bash'); - -function log(pattern) { - // console.log('---'); - console.log('equal(\'' + pattern + '\', ', util.inspect(braces(pattern).sort()).split('\n').join('') + ');'); - // console.log('equal(\'' + pattern + '\', ', util.inspect(bash(pattern).sort()).split('\n').join('') + ');'); - // console.log('equal(\'' + pattern + '\', ', util.inspect(expand(pattern).sort()).split('\n').join('') + ');'); - // console.log(expand(pattern)); - console.log(); -} - -log('{,eno,thro,ro}ugh') -log('{,{,eno,thro,ro}ugh}{,out}') -log('{{,eno,thro,ro}ugh,}{,out}') -log('{,{,a,b}z}{,c}') -log('{,{,a,b}z}{c,}') -log('{,{,a,b}z}{,c,}') -log('{,{,a,b}z}{c,d}') -log('{{,a,b}z,}{,c}') -log('{,a{,b}z,}{,c}') -log('{,a{,b},}{,c}') -log('{,a{,b}}{,c}') -log('{,b}{,d}') -log('{a,b}{,d}') -log('{,a}{z,c}') -log('{,{,a},}{z,c}') -log('{,,a,}{z,c}') -log('{{,a},}{z,c}') -log('{,{,a},}{z,c}') -log('{,{,a}}{z,c}') -log('{{a,},}{z,c}') -log('{{,a},}{z,c}') -log('{,,a}{z,c}') -log('{,a,}{z,c}') -log('{,{,}}{z,c}') -log('{,{a,b}}{,c}') -log('{,{a,}}{,c}') -log('{,{,b}}{,c}') -log('{,{,}}{,c}') -log('{,a}{,c}') -log('{,{,a}b}') -log('{,b}') -log('{,b{,a}}') -log('{b,{,a}}') -log('{,b}{,d}') -log('{a,b}{,d}') diff --git a/examples/options.quantifiers.js b/examples/options.quantifiers.js deleted file mode 100644 index ff79a57..0000000 --- a/examples/options.quantifiers.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var mm = require('minimatch'); -var braces = require('..'); - -/** - * True - */ - -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true})); -//=> [ 'a/b(1|3)/(x|y|z)' ] -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true})); -//=> [ 'a/b{1,3}/(x|y|z)' ] -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true})); -//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ] - -/** - * False - */ - -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false})); -//=> [ 'a/b(1|3)/(x|y|z)' ] -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false})); -//=> [ 'a/b{1,3}/(x|y|z)' ] -console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false, expand: true})); -//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ] diff --git a/examples/parse.js b/examples/parse.js new file mode 100644 index 0000000..1adf666 --- /dev/null +++ b/examples/parse.js @@ -0,0 +1,25 @@ +'use strict'; + +// const input = 'foo/{a,bar/{b,c},d}'; +// const input = 'a/{b,c{x,y}}/d'; +// const input = '{{x,y},/{b,c{x,y}d,e}/f}'; +// const input = '{{a,b}/{b,c{x,y}d,e}/f,x,z}'; +// const input = 'a/{b,c}/d'; +// const ast = parse(input); +// console.log(ast) +// console.log(JSON.stringify(ast.queue)); +// console.log('EXPECTED:', [ 'a/b/f', 'a/cxd/f', 'a/cyd/f', 'a/e/f' ]); +// console.log(JSON.stringify(ast, null, 2)) +// console.log(expand(ast)); +// expand(ast); + +// const sets = parse('foo/{a/b,{c,d,{x..z},e},f}/bar'); +// const sets = parse('{a,{c,d}'); +// console.log(sets.nodes[2]); +// console.log(compile(sets)); + +// const range = parse(']{a..e,z}'); +// console.log(range.nodes[2]); +// console.log(braces.expand(']{a..e,z}')) +// console.log(compile(range)); +// console.log(parse('[abc]')) diff --git a/examples/paths.js b/examples/paths.js deleted file mode 100644 index b759bb8..0000000 --- a/examples/paths.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -var braces = require('..'); - -console.log(braces('foo*{/*,*}')); -console.log(braces('foo (abc, 1990)')); diff --git a/examples/readme.js b/examples/readme.js deleted file mode 100644 index 53131a7..0000000 --- a/examples/readme.js +++ /dev/null @@ -1,28 +0,0 @@ -var braces = require('..'); - -console.log(braces.expand('it{,{{em,alic}iz,erat}e{d,}}')); -console.log(braces.expand('I like {pizza,beer,money}.')); -// console.log(braces.expand('{bull,shoe}{,horn}')); - -console.log(braces.expand('{a,b}{1,2}')); -//=> [ 'a1', 'a2', 'b1', 'b2' ] - -console.log(braces.expand('{a,b}/{1,2}')); -//=> [ 'a/1', 'a/2', 'b/1', 'b/2' ] - -console.log(braces.expand('foo/{a,b,c}/bar').join(' ')); -console.log(braces('{a,b}{1,2}')); -//=> [ '(a|b)(1|2)' ] - -console.log(braces.expand('{a,b,c}{1,2}').join(' ')); -console.log(braces.expand('{4..-4}').join(' ')); -//=> [ '(a|b)/(1|2)' ] - -console.log(braces.expand('{a,b,c}{1..3}').join(' ')); -console.log(braces.expand('{a..j}').join(' ')); -console.log(braces.expand('{j..a}').join(' ')); -console.log(braces.expand('{1..20..3}').join(' ')); -console.log(braces.expand('{a..z..3}').join(' ')); -console.log(braces.expand('a{1..3}b').join(' ')); -console.log(braces.expand('{1..3}.{0..9}0')); -//=> [ 'a1b', 'a2b', 'a3b' ] diff --git a/examples/regex.js b/examples/regex.js deleted file mode 100644 index 711e8e2..0000000 --- a/examples/regex.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var mm = require('minimatch'); -var braces = require('..'); - -// console.log(braces.makeRe('a/b/c/{k,l,m}/d/{w,x,y,z}/d/e', {expand: true})); -// console.log(mm.makeRe('a/b/c/{k,l,m}/d/{w,x,y,z}/d/e')); - -// console.log('braces.makeRe: "foo/{1..20000}/bar/{a..j}/baz"'); -// console.log(braces.makeRe('foo/{1..20000}/bar/{a..j}/baz')); -// console.log(); -// console.log('minimatch.makeRe: "foo/{1..20000}/bar/{a..j}/baz"'); -// console.log(mm.makeRe('foo/{1..20000}/bar/{a..j}/baz')); - -// var re = braces.makeRe('a/{foo/bar}/z'); -// console.log(re); -// console.log(re.test('a/{foo/bar}/z')); - -console.log(braces.makeRe('{00000001..99999999}')); diff --git a/examples/sequences-steps.js b/examples/sequences-steps.js deleted file mode 100644 index 6895aa9..0000000 --- a/examples/sequences-steps.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var mm = require('minimatch'); -var braces = require('..'); - -console.log(braces('{0..10..2}', {expand: true})); -//=> [ '0', '2', '4', '6', '8', '10' ] diff --git a/examples/sequences.js b/examples/sequences.js deleted file mode 100644 index 88dcf88..0000000 --- a/examples/sequences.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -var mm = require('minimatch'); -var braces = require('..'); - -console.log(braces('{1..5}', {expand: true})); -//=> [ '1', '2', '3', '4', '5' ] - -console.log(braces('{"1..5"}', {expand: true})) diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 0f8b221..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -var gulp = require('gulp'); -var mocha = require('gulp-mocha'); -var unused = require('gulp-unused'); -var istanbul = require('gulp-istanbul'); -var eslint = require('gulp-eslint'); - -gulp.task('coverage', function() { - return gulp.src(['index.js', 'lib/*.js']) - .pipe(istanbul()) - .pipe(istanbul.hookRequire()); -}); - -gulp.task('test', ['coverage'], function() { - return gulp.src('test/*.js') - .pipe(mocha({reporter: 'spec'})) - .pipe(istanbul.writeReports()); -}); - -gulp.task('eslint', function() { - return gulp.src(['*.js', 'lib/*.js', 'test/*.js']) - .pipe(eslint()) - .pipe(eslint.format()); -}); - -gulp.task('unused', function() { - return gulp.src(['index.js', 'lib/*.js']) - .pipe(unused({keys: Object.keys(require('./lib/utils.js'))})); -}); - -gulp.task('default', ['test', 'eslint']); diff --git a/index.js b/index.js index 048e1c2..9a05d47 100644 --- a/index.js +++ b/index.js @@ -1,34 +1,17 @@ 'use strict'; -/** - * Module dependencies - */ - -var toRegex = require('to-regex'); -var unique = require('array-unique'); -var extend = require('extend-shallow'); - -/** - * Local dependencies - */ - -var compilers = require('./lib/compilers'); -var parsers = require('./lib/parsers'); -var Braces = require('./lib/braces'); -var utils = require('./lib/utils'); -var MAX_LENGTH = 1024 * 64; -var cache = {}; +const stringify = require('./lib/stringify'); +const compile = require('./lib/compile'); +const expand = require('./lib/expand'); +const parse = require('./lib/parse'); /** - * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)). + * Expand the given pattern or create a regex-compatible string. * * ```js - * var braces = require('braces'); - * console.log(braces('{a,b,c}')); - * //=> ['(a|b|c)'] - * - * console.log(braces('{a,b,c}', {expand: true})); - * //=> ['a', 'b', 'c'] + * const braces = require('braces'); + * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)'] + * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c'] * ``` * @param {String} `str` * @param {Object} `options` @@ -36,283 +19,154 @@ var cache = {}; * @api public */ -function braces(pattern, options) { - var key = utils.createKey(String(pattern), options); - var arr = []; - - var disabled = options && options.cache === false; - if (!disabled && cache.hasOwnProperty(key)) { - return cache[key]; - } +const braces = (input, options = {}) => { + let result = []; - if (Array.isArray(pattern)) { - for (var i = 0; i < pattern.length; i++) { - arr.push.apply(arr, braces.create(pattern[i], options)); + if (Array.isArray(input)) { + for (let i = 0; i < input.length; i++) { + result.push(...braces.create(input[i], options)); } } else { - arr = braces.create(pattern, options); + result = braces.create(input, options); } if (options && options.nodupes === true) { - arr = unique(arr); + result = [...new Set(result)]; } - if (!disabled) { - cache[key] = arr; - } - return arr; -} + return result; +}; /** - * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead. + * Parse the given `str` with the given `options`. * * ```js - * var braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/b/d', 'a/c/d']; + * // braces.parse(pattern, [, options]); + * const ast = braces.parse('a/{b,c}/d'); + * console.log(ast); * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. + * @param {String} pattern Brace pattern to parse + * @param {Object} options + * @return {Object} Returns an AST * @api public */ -braces.expand = function(pattern, options) { - return braces.create(pattern, extend({}, options, {expand: true})); -}; +braces.parse = (input, options = {}) => parse(input, options); /** - * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default. + * Creates a braces string from an AST, or an AST node. * * ```js - * var braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/(b|c)/d'] + * const braces = require('braces'); + * let ast = braces.parse('foo/{a,b}/bar'); + * console.log(stringify(ast.nodes[2])); //=> '{a,b}' * ``` - * @param {String} `pattern` Brace pattern + * @param {String} `input` Brace pattern or AST. * @param {Object} `options` * @return {Array} Returns an array of expanded values. * @api public */ -braces.optimize = function(pattern, options) { - return braces.create(pattern, options); +braces.stringify = (input, options = {}) => { + if (typeof input === 'string') { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); }; /** - * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function. + * Compiles a brace pattern into a regex-compatible, optimized string. + * This method is called by the main [braces](#braces) function by default. * * ```js - * var braces = require('braces'); - * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) - * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' + * const braces = require('braces'); + * console.log(braces.compile('a/{b,c}/d')); + * //=> ['a/(b|c)/d'] * ``` - * @param {String} `pattern` Brace pattern + * @param {String} `input` Brace pattern or AST. * @param {Object} `options` * @return {Array} Returns an array of expanded values. * @api public */ -braces.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); +braces.compile = (input, options = {}) => { + if (typeof input === 'string') { + return compile(braces.parse(input, options), options); } - - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); - } - - function create() { - if (pattern === '' || pattern.length < 3) { - return [pattern]; - } - - if (utils.isEmptySets(pattern)) { - return []; - } - - if (utils.isQuotedString(pattern)) { - return [pattern.slice(1, -1)]; - } - - var proto = new Braces(options); - var result = !options || options.expand !== true - ? proto.optimize(pattern, options) - : proto.expand(pattern, options); - - // get the generated pattern(s) - var arr = result.output; - - // filter out empty strings if specified - if (options && options.noempty === true) { - arr = arr.filter(Boolean); - } - - // filter out duplicates if specified - if (options && options.nodupes === true) { - arr = unique(arr); - } - - Object.defineProperty(arr, 'result', { - enumerable: false, - value: result - }); - - return arr; - } - - return memoize('create', pattern, options, create); + return compile(input, options); }; /** - * Create a regular expression from the given string `pattern`. + * Expands a brace pattern into an array. This method is called by the + * main [braces](#braces) function when `options.expand` is true. Before + * using this method it's recommended that you read the [performance notes](#performance)) + * and advantages of using [.compile](#compile) instead. * * ```js - * var braces = require('braces'); - * - * console.log(braces.makeRe('id-{200..300}')); - * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/ + * const braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/b/d', 'a/c/d']; * ``` - * @param {String} `pattern` The pattern to convert to regex. + * @param {String} `pattern` Brace pattern * @param {Object} `options` - * @return {RegExp} + * @return {Array} Returns an array of expanded values. * @api public */ -braces.makeRe = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); +braces.expand = (input, options = {}) => { + if (typeof input === 'string') { + input = braces.parse(input, options); } - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); - } + let result = expand(input, options); - function makeRe() { - var arr = braces(pattern, options); - var opts = extend({strictErrors: false}, options); - return toRegex(arr, opts); + // filter out duplicates if specified + if (options.nodupes === true) { + result = [...new Set(result)]; } - - return memoize('makeRe', pattern, options, makeRe); -}; - -/** - * Parse the given `str` with the given `options`. - * - * ```js - * var braces = require('braces'); - * var ast = braces.parse('a/{b,c}/d'); - * console.log(ast); - * // { type: 'root', - * // errors: [], - * // input: 'a/{b,c}/d', - * // nodes: - * // [ { type: 'bos', val: '' }, - * // { type: 'text', val: 'a/' }, - * // { type: 'brace', - * // nodes: - * // [ { type: 'brace.open', val: '{' }, - * // { type: 'text', val: 'b,c' }, - * // { type: 'brace.close', val: '}' } ] }, - * // { type: 'text', val: '/d' }, - * // { type: 'eos', val: '' } ] } - * ``` - * @param {String} `pattern` Brace pattern to parse - * @param {Object} `options` - * @return {Object} Returns an AST - * @api public - */ - -braces.parse = function(pattern, options) { - var proto = new Braces(options); - return proto.parse(pattern, options); + return result; }; /** - * Compile the given `ast` or string with the given `options`. + * Processes a brace pattern and returns either an expanded array + * (if `options.expand` is true), a highly optimized regex-compatible string. + * This method is called by the main [braces](#braces) function. * * ```js - * var braces = require('braces'); - * var ast = braces.parse('a/{b,c}/d'); - * console.log(braces.compile(ast)); - * // { options: { source: 'string' }, - * // state: {}, - * // compilers: - * // { eos: [Function], - * // noop: [Function], - * // bos: [Function], - * // brace: [Function], - * // 'brace.open': [Function], - * // text: [Function], - * // 'brace.close': [Function] }, - * // output: [ 'a/(b|c)/d' ], - * // ast: - * // { ... }, - * // parsingErrors: [] } + * const braces = require('braces'); + * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) + * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' * ``` - * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first. + * @param {String} `pattern` Brace pattern * @param {Object} `options` - * @return {Object} Returns an object that has an `output` property with the compiled string. - * @api public - */ - -braces.compile = function(ast, options) { - var proto = new Braces(options); - return proto.compile(ast, options); -}; - -/** - * Clear the regex cache. - * - * ```js - * braces.clearCache(); - * ``` + * @return {Array} Returns an array of expanded values. * @api public */ -braces.clearCache = function() { - cache = braces.cache = {}; -}; +braces.create = (input, options = {}) => { + if (input === '' || input.length < 3) { + return [input]; + } -/** - * Memoize a generated regex or function. A unique key is generated - * from the method name, pattern, and user-defined options. Set - * options.memoize to false to disable. - */ + let result = options.expand !== true + ? braces.compile(input, options) + : braces.expand(input, options); -function memoize(type, pattern, options, fn) { - var key = utils.createKey(type + ':' + pattern, options); - var disabled = options && options.cache === false; - if (disabled) { - braces.clearCache(); - return fn(pattern, options); + // filter out empty strings if specified + if (options.noempty === true) { + result = result.filter(Boolean); } - if (cache.hasOwnProperty(key)) { - return cache[key]; + // filter out duplicates if specified + if (options.nodupes === true) { + result = [...new Set(result)]; } - var res = fn(pattern, options); - cache[key] = res; - return res; -} - -/** - * Expose `Braces` constructor and methods - * @type {Function} - */ - -braces.Braces = Braces; -braces.compilers = compilers; -braces.parsers = parsers; -braces.cache = cache; + return result; +}; /** - * Expose `braces` - * @type {Function} + * Expose "braces" */ module.exports = braces; diff --git a/lib/braces.js b/lib/braces.js deleted file mode 100644 index baf6bf1..0000000 --- a/lib/braces.js +++ /dev/null @@ -1,104 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); -var Snapdragon = require('snapdragon'); -var compilers = require('./compilers'); -var parsers = require('./parsers'); -var utils = require('./utils'); - -/** - * Customize Snapdragon parser and renderer - */ - -function Braces(options) { - this.options = extend({}, options); -} - -/** - * Initialize braces - */ - -Braces.prototype.init = function(options) { - if (this.isInitialized) return; - this.isInitialized = true; - var opts = utils.createOptions({}, this.options, options); - this.snapdragon = this.options.snapdragon || new Snapdragon(opts); - this.compiler = this.snapdragon.compiler; - this.parser = this.snapdragon.parser; - - compilers(this.snapdragon, opts); - parsers(this.snapdragon, opts); - - /** - * Call Snapdragon `.parse` method. When AST is returned, we check to - * see if any unclosed braces are left on the stack and, if so, we iterate - * over the stack and correct the AST so that compilers are called in the correct - * order and unbalance braces are properly escaped. - */ - - utils.define(this.snapdragon, 'parse', function(pattern, options) { - var parsed = Snapdragon.prototype.parse.apply(this, arguments); - this.parser.ast.input = pattern; - - var stack = this.parser.stack; - while (stack.length) { - addParent({type: 'brace.close', val: ''}, stack.pop()); - } - - function addParent(node, parent) { - utils.define(node, 'parent', parent); - parent.nodes.push(node); - } - - // add non-enumerable parser reference - utils.define(parsed, 'parser', this.parser); - return parsed; - }); -}; - -/** - * Decorate `.parse` method - */ - -Braces.prototype.parse = function(ast, options) { - if (ast && typeof ast === 'object' && ast.nodes) return ast; - this.init(options); - return this.snapdragon.parse(ast, options); -}; - -/** - * Decorate `.compile` method - */ - -Braces.prototype.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = this.parse(ast, options); - } else { - this.init(options); - } - return this.snapdragon.compile(ast, options); -}; - -/** - * Expand - */ - -Braces.prototype.expand = function(pattern) { - var ast = this.parse(pattern, {expand: true}); - return this.compile(ast, {expand: true}); -}; - -/** - * Optimize - */ - -Braces.prototype.optimize = function(pattern) { - var ast = this.parse(pattern, {optimize: true}); - return this.compile(ast, {optimize: true}); -}; - -/** - * Expose `Braces` - */ - -module.exports = Braces; diff --git a/lib/compile.js b/lib/compile.js new file mode 100644 index 0000000..3e984a4 --- /dev/null +++ b/lib/compile.js @@ -0,0 +1,57 @@ +'use strict'; + +const fill = require('fill-range'); +const utils = require('./utils'); + +const compile = (ast, options = {}) => { + let walk = (node, parent = {}) => { + let invalidBlock = utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let invalid = invalidBlock === true || invalidNode === true; + let prefix = options.escapeInvalid === true ? '\\' : ''; + let output = ''; + + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + return prefix + node.value; + } + + if (node.type === 'open') { + return invalid ? (prefix + node.value) : '('; + } + + if (node.type === 'close') { + return invalid ? (prefix + node.value) : ')'; + } + + if (node.type === 'comma') { + return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|'); + } + + if (node.value) { + return node.value; + } + + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + let range = fill(...args, { ...options, wrap: false, toRegex: true }); + + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } + } + + if (node.nodes) { + for (let child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; + + return walk(ast); +}; + +module.exports = compile; diff --git a/lib/compilers.js b/lib/compilers.js deleted file mode 100644 index a3b820e..0000000 --- a/lib/compilers.js +++ /dev/null @@ -1,282 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -module.exports = function(braces, options) { - braces.compiler - - /** - * bos - */ - - .set('bos', function() { - if (this.output) return; - this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : []; - this.ast.count = 1; - }) - - /** - * Square brackets - */ - - .set('bracket', function(node) { - var close = node.close; - var open = !node.escaped ? '[' : '\\['; - var negated = node.negated; - var inner = node.inner; - - inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\'); - if (inner === ']-') { - inner = '\\]\\-'; - } - - if (negated && inner.indexOf('.') === -1) { - inner += '.'; - } - if (negated && inner.indexOf('/') === -1) { - inner += '/'; - } - - var val = open + negated + inner + close; - var queue = node.parent.queue; - var last = utils.arrayify(queue.pop()); - - queue.push(utils.join(last, val)); - queue.push.apply(queue, []); - }) - - /** - * Brace - */ - - .set('brace', function(node) { - node.queue = isEscaped(node) ? [node.val] : []; - node.count = 1; - return this.mapVisit(node.nodes); - }) - - /** - * Open - */ - - .set('brace.open', function(node) { - node.parent.open = node.val; - }) - - /** - * Inner - */ - - .set('text', function(node) { - var queue = node.parent.queue; - var escaped = node.escaped; - var segs = [node.val]; - - if (node.optimize === false) { - options = utils.extend({}, options, {optimize: false}); - } - - if (node.multiplier > 1) { - node.parent.count *= node.multiplier; - } - - if (options.quantifiers === true && utils.isQuantifier(node.val)) { - escaped = true; - - } else if (node.val.length > 1) { - if (isType(node.parent, 'brace') && !isEscaped(node)) { - var expanded = utils.expand(node.val, options); - segs = expanded.segs; - - if (expanded.isOptimized) { - node.parent.isOptimized = true; - } - - // if nothing was expanded, we probably have a literal brace - if (!segs.length) { - var val = (expanded.val || node.val); - if (options.unescape !== false) { - // unescape unexpanded brace sequence/set separators - val = val.replace(/\\([,.])/g, '$1'); - // strip quotes - val = val.replace(/["'`]/g, ''); - } - - segs = [val]; - escaped = true; - } - } - - } else if (node.val === ',') { - if (options.expand) { - node.parent.queue.push(['']); - segs = ['']; - } else { - segs = ['|']; - } - } else { - escaped = true; - } - - if (escaped && isType(node.parent, 'brace')) { - if (node.parent.nodes.length <= 4 && node.parent.count === 1) { - node.parent.escaped = true; - } else if (node.parent.length <= 3) { - node.parent.escaped = true; - } - } - - if (!hasQueue(node.parent)) { - node.parent.queue = segs; - return; - } - - var last = utils.arrayify(queue.pop()); - if (node.parent.count > 1 && options.expand) { - last = multiply(last, node.parent.count); - node.parent.count = 1; - } - - queue.push(utils.join(utils.flatten(last), segs.shift())); - queue.push.apply(queue, segs); - }) - - /** - * Close - */ - - .set('brace.close', function(node) { - var queue = node.parent.queue; - var prev = node.parent.parent; - var last = prev.queue.pop(); - var open = node.parent.open; - var close = node.val; - - if (open && close && isOptimized(node, options)) { - open = '('; - close = ')'; - } - - // if a close brace exists, and the previous segment is one character - // don't wrap the result in braces or parens - var ele = utils.last(queue); - if (node.parent.count > 1 && options.expand) { - ele = multiply(queue.pop(), node.parent.count); - node.parent.count = 1; - queue.push(ele); - } - - if (close && typeof ele === 'string' && ele.length === 1) { - open = ''; - close = ''; - } - - if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) { - queue.push(utils.join(open, queue.pop() || '')); - queue = utils.flatten(utils.join(queue, close)); - } - - if (typeof last === 'undefined') { - prev.queue = [queue]; - } else { - prev.queue.push(utils.flatten(utils.join(last, queue))); - } - }) - - /** - * eos - */ - - .set('eos', function(node) { - if (this.input) return; - - if (options.optimize !== false) { - this.output = utils.last(utils.flatten(this.ast.queue)); - } else if (Array.isArray(utils.last(this.ast.queue))) { - this.output = utils.flatten(this.ast.queue.pop()); - } else { - this.output = utils.flatten(this.ast.queue); - } - - if (node.parent.count > 1 && options.expand) { - this.output = multiply(this.output, node.parent.count); - } - - this.output = utils.arrayify(this.output); - this.ast.queue = []; - }); - -}; - -/** - * Multiply the segments in the current brace level - */ - -function multiply(queue, n, options) { - return utils.flatten(utils.repeat(utils.arrayify(queue), n)); -} - -/** - * Return true if `node` is escaped - */ - -function isEscaped(node) { - return node.escaped === true; -} - -/** - * Returns true if regex parens should be used for sets. If the parent `type` - * is not `brace`, then we're on a root node, which means we should never - * expand segments and open/close braces should be `{}` (since this indicates - * a brace is missing from the set) - */ - -function isOptimized(node, options) { - if (node.parent.isOptimized) return true; - return isType(node.parent, 'brace') - && !isEscaped(node.parent) - && options.expand !== true; -} - -/** - * Returns true if the value in `node` should be wrapped in a literal brace. - * @return {Boolean} - */ - -function isLiteralBrace(node, options) { - return isEscaped(node.parent) || options.optimize !== false; -} - -/** - * Returns true if the given `node` does not have an inner value. - * @return {Boolean} - */ - -function noInner(node, type) { - if (node.parent.queue.length === 1) { - return true; - } - var nodes = node.parent.nodes; - return nodes.length === 3 - && isType(nodes[0], 'brace.open') - && !isType(nodes[1], 'text') - && isType(nodes[2], 'brace.close'); -} - -/** - * Returns true if the given `node` is the given `type` - * @return {Boolean} - */ - -function isType(node, type) { - return typeof node !== 'undefined' && node.type === type; -} - -/** - * Returns true if the given `node` has a non-empty queue. - * @return {Boolean} - */ - -function hasQueue(node) { - return Array.isArray(node.queue) && node.queue.length; -} diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 0000000..a937943 --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,57 @@ +'use strict'; + +module.exports = { + MAX_LENGTH: 1024 * 64, + + // Digits + CHAR_0: '0', /* 0 */ + CHAR_9: '9', /* 9 */ + + // Alphabet chars. + CHAR_UPPERCASE_A: 'A', /* A */ + CHAR_LOWERCASE_A: 'a', /* a */ + CHAR_UPPERCASE_Z: 'Z', /* Z */ + CHAR_LOWERCASE_Z: 'z', /* z */ + + CHAR_LEFT_PARENTHESES: '(', /* ( */ + CHAR_RIGHT_PARENTHESES: ')', /* ) */ + + CHAR_ASTERISK: '*', /* * */ + + // Non-alphabetic chars. + CHAR_AMPERSAND: '&', /* & */ + CHAR_AT: '@', /* @ */ + CHAR_BACKSLASH: '\\', /* \ */ + CHAR_BACKTICK: '`', /* ` */ + CHAR_CARRIAGE_RETURN: '\r', /* \r */ + CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */ + CHAR_COLON: ':', /* : */ + CHAR_COMMA: ',', /* , */ + CHAR_DOLLAR: '$', /* . */ + CHAR_DOT: '.', /* . */ + CHAR_DOUBLE_QUOTE: '"', /* " */ + CHAR_EQUAL: '=', /* = */ + CHAR_EXCLAMATION_MARK: '!', /* ! */ + CHAR_FORM_FEED: '\f', /* \f */ + CHAR_FORWARD_SLASH: '/', /* / */ + CHAR_HASH: '#', /* # */ + CHAR_HYPHEN_MINUS: '-', /* - */ + CHAR_LEFT_ANGLE_BRACKET: '<', /* < */ + CHAR_LEFT_CURLY_BRACE: '{', /* { */ + CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */ + CHAR_LINE_FEED: '\n', /* \n */ + CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */ + CHAR_PERCENT: '%', /* % */ + CHAR_PLUS: '+', /* + */ + CHAR_QUESTION_MARK: '?', /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */ + CHAR_RIGHT_CURLY_BRACE: '}', /* } */ + CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */ + CHAR_SEMICOLON: ';', /* ; */ + CHAR_SINGLE_QUOTE: '\'', /* ' */ + CHAR_SPACE: ' ', /* */ + CHAR_TAB: '\t', /* \t */ + CHAR_UNDERSCORE: '_', /* _ */ + CHAR_VERTICAL_LINE: '|', /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */ +}; diff --git a/lib/expand.js b/lib/expand.js new file mode 100644 index 0000000..376c748 --- /dev/null +++ b/lib/expand.js @@ -0,0 +1,113 @@ +'use strict'; + +const fill = require('fill-range'); +const stringify = require('./stringify'); +const utils = require('./utils'); + +const append = (queue = '', stash = '', enclose = false) => { + let result = []; + + queue = [].concat(queue); + stash = [].concat(stash); + + if (!stash.length) return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash; + } + + for (let item of queue) { + if (Array.isArray(item)) { + for (let value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === 'string') ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele)); + } + } + } + return utils.flatten(result); +}; + +const expand = (ast, options = {}) => { + let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit; + + let walk = (node, parent = {}) => { + node.queue = []; + + let p = parent; + let q = parent.queue; + + while (p.type !== 'brace' && p.type !== 'root' && p.parent) { + p = p.parent; + q = p.queue; + } + + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; + } + + if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ['{}'])); + return; + } + + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); + } + + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); + } + + q.push(append(q.pop(), range)); + node.nodes = []; + return; + } + + let enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; + + while (block.type !== 'brace' && block.type !== 'root' && block.parent) { + block = block.parent; + queue = block.queue; + } + + for (let i = 0; i < node.nodes.length; i++) { + let child = node.nodes[i]; + + if (child.type === 'comma' && node.type === 'brace') { + if (i === 1) queue.push(''); + queue.push(''); + continue; + } + + if (child.type === 'close') { + q.push(append(q.pop(), queue, enclose)); + continue; + } + + if (child.value && child.type !== 'open') { + queue.push(append(queue.pop(), child.value)); + continue; + } + + if (child.nodes) { + walk(child, node); + } + } + + return queue; + }; + + return utils.flatten(walk(ast)); +}; + +module.exports = expand; diff --git a/lib/parse.js b/lib/parse.js new file mode 100644 index 0000000..d7d135e --- /dev/null +++ b/lib/parse.js @@ -0,0 +1,326 @@ +'use strict'; + +const stringify = require('./stringify'); + +/** + * Constants + */ + +const { + MAX_LENGTH, + CHAR_BACKSLASH, /* \ */ + CHAR_BACKTICK, /* ` */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, /* ] */ + CHAR_DOUBLE_QUOTE, /* " */ + CHAR_SINGLE_QUOTE, /* ' */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE +} = require('./constants'); + +/** + * parse + */ + +const parse = (input, options = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } + + let opts = options || {}; + let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } + + let ast = { type: 'root', input, nodes: [] }; + let stack = [ast]; + let block = ast; + let prev = ast; + let length = input.length; + let index = 0; + let depth = 0; + let value; + let memo = {}; + + /** + * Helpers + */ + + const advance = () => input[index++]; + const push = node => { + if (node.type === 'text' && prev.type === 'dot') { + prev.type = 'text'; + } + + if (prev && prev.type === 'text' && node.type === 'text') { + prev.value += node.value; + return; + } + + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; + + push({ type: 'bos' }); + + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); + + /** + * Invalid chars + */ + + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } + + /** + * Escaped chars + */ + + if (value === CHAR_BACKSLASH) { + push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() }); + continue; + } + + /** + * Right square bracket (literal): ']' + */ + + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: 'text', value: '\\' + value }); + continue; + } + + /** + * Left square bracket: '[' + */ + + if (value === CHAR_LEFT_SQUARE_BRACKET) { + let closed = true; + let next; + + while (index < length && (next = advance())) { + value += next; + + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } + + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + closed = true; + break; + } + } + + if (closed !== true) { + value = '\\' + value; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Parentheses + */ + + if (value === CHAR_LEFT_PARENTHESES) { + block = push({ type: 'paren', nodes: [] }); + stack.push(block); + push({ type: 'text', value }); + continue; + } + + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== 'paren') { + push({ type: 'text', value }); + continue; + } + block = stack.pop(); + push({ type: 'text', value }); + block = stack[stack.length - 1]; + continue; + } + + /** + * Quotes: '|"|` + */ + + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + let open = value; + let next; + + if (options.keepQuotes !== true) { + value = ''; + } + + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } + + if (next === open) { + if (options.keepQuotes === true) value += next; + break; + } + + value += next; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Left curly brace: '{' + */ + + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + + let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true; + let brace = { + type: 'brace', + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; + + block = push(brace); + stack.push(block); + push({ type: 'open', value }); + continue; + } + + /** + * Right curly brace: '}' + */ + + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== 'brace') { + push({ type: 'text', value }); + continue; + } + + let type = 'close'; + block = stack.pop(); + block.close = true; + + push({ type, value }); + depth--; + + block = stack[stack.length - 1]; + continue; + } + + /** + * Comma: ',' + */ + + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + let open = block.nodes.shift(); + block.nodes = [open, { type: 'text', value: stringify(block) }]; + } + + push({ type: 'comma', value }); + block.commas++; + continue; + } + + /** + * Dot: '.' + */ + + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + let siblings = block.nodes; + + if (depth === 0 || siblings.length === 0) { + push({ type: 'text', value }); + continue; + } + + if (prev.type === 'dot') { + block.range = []; + prev.value += value; + prev.type = 'range'; + + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = 'text'; + continue; + } + + block.ranges++; + block.args = []; + continue; + } + + if (prev.type === 'range') { + siblings.pop(); + + let before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } + + push({ type: 'dot', value }); + continue; + } + + /** + * Text + */ + + push({ type: 'text', value }); + } + + // Mark imbalanced braces and brackets as invalid + do { + block = stack.pop(); + + if (block.type !== 'root') { + block.nodes.forEach(node => { + if (!node.nodes) { + if (node.type === 'open') node.isOpen = true; + if (node.type === 'close') node.isClose = true; + if (!node.nodes) node.type = 'text'; + node.invalid = true; + } + }); + + // get the location of the block on parent.nodes (block's siblings) + let parent = stack[stack.length - 1]; + let index = parent.nodes.indexOf(block); + // replace the (invalid) block with it's nodes + parent.nodes.splice(index, 1, ...block.nodes); + } + } while (stack.length > 0); + + push({ type: 'eos' }); + return ast; +}; + +module.exports = parse; diff --git a/lib/parsers.js b/lib/parsers.js deleted file mode 100644 index 8bf3e92..0000000 --- a/lib/parsers.js +++ /dev/null @@ -1,360 +0,0 @@ -'use strict'; - -var Node = require('snapdragon-node'); -var utils = require('./utils'); - -/** - * Braces parsers - */ - -module.exports = function(braces, options) { - braces.parser - .set('bos', function() { - if (!this.parsed) { - this.ast = this.nodes[0] = new Node(this.ast); - } - }) - - /** - * Character parsers - */ - - .set('escape', function() { - var pos = this.position(); - var m = this.match(/^(?:\\(.)|\$\{)/); - if (!m) return; - - var prev = this.prev(); - var last = utils.last(prev.nodes); - - var node = pos(new Node({ - type: 'text', - multiplier: 1, - val: m[0] - })); - - if (node.val === '\\\\') { - return node; - } - - if (node.val === '${') { - var str = this.input; - var idx = -1; - var ch; - - while ((ch = str[++idx])) { - this.consume(1); - node.val += ch; - if (ch === '\\') { - node.val += str[++idx]; - continue; - } - if (ch === '}') { - break; - } - } - } - - if (this.options.unescape !== false) { - node.val = node.val.replace(/\\([{}])/g, '$1'); - } - - if (last.val === '"' && this.input.charAt(0) === '"') { - last.val = node.val; - this.consume(1); - return; - } - - return concatNodes.call(this, pos, node, prev, options); - }) - - /** - * Brackets: "[...]" (basic, this is overridden by - * other parsers in more advanced implementations) - */ - - .set('bracket', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/); - if (!m) return; - - var prev = this.prev(); - var val = m[0]; - var negated = m[1] ? '^' : ''; - var inner = m[2] || ''; - var close = m[3] || ''; - - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } - - var esc = this.input.slice(0, 2); - if (inner === '' && esc === '\\]') { - inner += esc; - this.consume(2); - - var str = this.input; - var idx = -1; - var ch; - - while ((ch = str[++idx])) { - this.consume(1); - if (ch === ']') { - close = ch; - break; - } - inner += ch; - } - } - - return pos(new Node({ - type: 'bracket', - val: val, - escaped: close !== ']', - negated: negated, - inner: inner, - close: close - })); - }) - - /** - * Empty braces (we capture these early to - * speed up processing in the compiler) - */ - - .set('multiplier', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^\{((?:,|\{,+\})+)\}/); - if (!m) return; - - this.multiplier = true; - var prev = this.prev(); - var val = m[0]; - - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } - - var node = pos(new Node({ - type: 'text', - multiplier: 1, - match: m, - val: val - })); - - return concatNodes.call(this, pos, node, prev, options); - }) - - /** - * Open - */ - - .set('brace.open', function() { - var pos = this.position(); - var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/); - if (!m) return; - - var prev = this.prev(); - var last = utils.last(prev.nodes); - - // if the last parsed character was an extglob character - // we need to _not optimize_ the brace pattern because - // it might be mistaken for an extglob by a downstream parser - if (last && last.val && isExtglobChar(last.val.slice(-1))) { - last.optimize = false; - } - - var open = pos(new Node({ - type: 'brace.open', - val: m[0] - })); - - var node = pos(new Node({ - type: 'brace', - nodes: [] - })); - - node.push(open); - prev.push(node); - this.push('brace', node); - }) - - /** - * Close - */ - - .set('brace.close', function() { - var pos = this.position(); - var m = this.match(/^\}/); - if (!m || !m[0]) return; - - var brace = this.pop('brace'); - var node = pos(new Node({ - type: 'brace.close', - val: m[0] - })); - - if (!this.isType(brace, 'brace')) { - if (this.options.strict) { - throw new Error('missing opening "{"'); - } - node.type = 'text'; - node.multiplier = 0; - node.escaped = true; - return node; - } - - var prev = this.prev(); - var last = utils.last(prev.nodes); - if (last.text) { - var lastNode = utils.last(last.nodes); - if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) { - var open = last.nodes[0]; - var text = last.nodes[1]; - if (open.type === 'brace.open' && text && text.type === 'text') { - text.optimize = false; - } - } - } - - if (brace.nodes.length > 2) { - var first = brace.nodes[1]; - if (first.type === 'text' && first.val === ',') { - brace.nodes.splice(1, 1); - brace.nodes.push(first); - } - } - - brace.push(node); - }) - - /** - * Capture boundary characters - */ - - .set('boundary', function() { - var pos = this.position(); - var m = this.match(/^[$^](?!\{)/); - if (!m) return; - return pos(new Node({ - type: 'text', - val: m[0] - })); - }) - - /** - * One or zero, non-comma characters wrapped in braces - */ - - .set('nobrace', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^\{[^,]?\}/); - if (!m) return; - - var prev = this.prev(); - var val = m[0]; - - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } - - return pos(new Node({ - type: 'text', - multiplier: 0, - val: val - })); - }) - - /** - * Text - */ - - .set('text', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^((?!\\)[^${}[\]])+/); - if (!m) return; - - var prev = this.prev(); - var val = m[0]; - - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } - - var node = pos(new Node({ - type: 'text', - multiplier: 1, - val: val - })); - - return concatNodes.call(this, pos, node, prev, options); - }); -}; - -/** - * Returns true if the character is an extglob character. - */ - -function isExtglobChar(ch) { - return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+'; -} - -/** - * Combine text nodes, and calculate empty sets (`{,,}`) - * @param {Function} `pos` Function to calculate node position - * @param {Object} `node` AST node - * @return {Object} - */ - -function concatNodes(pos, node, parent, options) { - node.orig = node.val; - var prev = this.prev(); - var last = utils.last(prev.nodes); - var isEscaped = false; - - if (node.val.length > 1) { - var a = node.val.charAt(0); - var b = node.val.slice(-1); - - isEscaped = (a === '"' && b === '"') - || (a === "'" && b === "'") - || (a === '`' && b === '`'); - } - - if (isEscaped && options.unescape !== false) { - node.val = node.val.slice(1, node.val.length - 1); - node.escaped = true; - } - - if (node.match) { - var match = node.match[1]; - if (!match || match.indexOf('}') === -1) { - match = node.match[0]; - } - - // replace each set with a single "," - var val = match.replace(/\{/g, ',').replace(/\}/g, ''); - node.multiplier *= val.length; - node.val = ''; - } - - var simpleText = last.type === 'text' - && last.multiplier === 1 - && node.multiplier === 1 - && node.val; - - if (simpleText) { - last.val += node.val; - return; - } - - prev.push(node); -} diff --git a/lib/stringify.js b/lib/stringify.js new file mode 100644 index 0000000..414b7bc --- /dev/null +++ b/lib/stringify.js @@ -0,0 +1,32 @@ +'use strict'; + +const utils = require('./utils'); + +module.exports = (ast, options = {}) => { + let stringify = (node, parent = {}) => { + let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ''; + + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return '\\' + node.value; + } + return node.value; + } + + if (node.value) { + return node.value; + } + + if (node.nodes) { + for (let child of node.nodes) { + output += stringify(child); + } + } + return output; + }; + + return stringify(ast); +}; + diff --git a/lib/utils.js b/lib/utils.js index 4716671..e3551a6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,343 +1,112 @@ 'use strict'; -var splitString = require('split-string'); -var utils = module.exports; - -/** - * Module dependencies - */ - -utils.extend = require('extend-shallow'); -utils.flatten = require('arr-flatten'); -utils.isObject = require('isobject'); -utils.fillRange = require('fill-range'); -utils.repeat = require('repeat-element'); -utils.unique = require('array-unique'); - -utils.define = function(obj, key, val) { - Object.defineProperty(obj, key, { - writable: true, - configurable: true, - enumerable: false, - value: val - }); -}; - -/** - * Returns true if the given string contains only empty brace sets. - */ - -utils.isEmptySets = function(str) { - return /^(?:\{,\})+$/.test(str); -}; - -/** - * Returns true if the given string contains only empty brace sets. - */ - -utils.isQuotedString = function(str) { - var open = str.charAt(0); - if (open === '\'' || open === '"' || open === '`') { - return str.slice(-1) === open; +exports.isInteger = num => { + if (typeof num === 'number') { + return Number.isInteger(num); + } + if (typeof num === 'string' && num.trim() !== '') { + return Number.isInteger(Number(num)); } return false; }; /** - * Create the key to use for memoization. The unique key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. + * Find a node of the given type */ -utils.createKey = function(pattern, options) { - var id = pattern; - if (typeof options === 'undefined') { - return id; - } - var keys = Object.keys(options); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - id += ';' + key + '=' + String(options[key]); - } - return id; -}; +exports.find = (node, type) => node.nodes.find(node => node.type === type); /** - * Normalize options + * Find a node of the given type */ -utils.createOptions = function(options) { - var opts = utils.extend.apply(null, arguments); - if (typeof opts.expand === 'boolean') { - opts.optimize = !opts.expand; - } - if (typeof opts.optimize === 'boolean') { - opts.expand = !opts.optimize; - } - if (opts.optimize === true) { - opts.makeRe = true; - } - return opts; +exports.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) return false; + if (!exports.isInteger(min) || !exports.isInteger(max)) return false; + return ((Number(max) - Number(min)) / Number(step)) >= limit; }; /** - * Join patterns in `a` to patterns in `b` + * Escape the given node with '\\' before node.value */ -utils.join = function(a, b, options) { - options = options || {}; - a = utils.arrayify(a); - b = utils.arrayify(b); - - if (!a.length) return b; - if (!b.length) return a; - - var len = a.length; - var idx = -1; - var arr = []; - - while (++idx < len) { - var val = a[idx]; - if (Array.isArray(val)) { - for (var i = 0; i < val.length; i++) { - val[i] = utils.join(val[i], b, options); - } - arr.push(val); - continue; - } - - for (var j = 0; j < b.length; j++) { - var bval = b[j]; +exports.escapeNode = (block, n = 0, type) => { + let node = block.nodes[n]; + if (!node) return; - if (Array.isArray(bval)) { - arr.push(utils.join(val, bval, options)); - } else { - arr.push(val + bval); - } + if ((type && node.type === type) || node.type === 'open' || node.type === 'close') { + if (node.escaped !== true) { + node.value = '\\' + node.value; + node.escaped = true; } } - return arr; }; /** - * Split the given string on `,` if not escaped. + * Returns true if the given brace node should be enclosed in literal braces */ -utils.split = function(str, options) { - var opts = utils.extend({sep: ','}, options); - if (typeof opts.keepQuotes !== 'boolean') { - opts.keepQuotes = true; - } - if (opts.unescape === false) { - opts.keepEscaping = true; +exports.encloseBrace = node => { + if (node.type !== 'brace') return false; + if ((node.commas >> 0 + node.ranges >> 0) === 0) { + node.invalid = true; + return true; } - return splitString(str, opts, utils.escapeBrackets(opts)); + return false; }; /** - * Expand ranges or sets in the given `pattern`. - * - * @param {String} `str` - * @param {Object} `options` - * @return {Object} + * Returns true if a brace node is invalid. */ -utils.expand = function(str, options) { - var opts = utils.extend({rangeLimit: 10000}, options); - var segs = utils.split(str, opts); - var tok = { segs: segs }; - - if (utils.isQuotedString(str)) { - return tok; +exports.isInvalidBrace = block => { + if (block.type !== 'brace') return false; + if (block.invalid === true || block.dollar) return true; + if ((block.commas >> 0 + block.ranges >> 0) === 0) { + block.invalid = true; + return true; } - - if (opts.rangeLimit === true) { - opts.rangeLimit = 10000; - } - - if (segs.length > 1) { - if (opts.optimize === false) { - tok.val = segs[0]; - return tok; - } - - tok.segs = utils.stringifyArray(tok.segs); - } else if (segs.length === 1) { - var arr = str.split('..'); - - if (arr.length === 1) { - tok.val = tok.segs[tok.segs.length - 1] || tok.val || str; - tok.segs = []; - return tok; - } - - if (arr.length === 2 && arr[0] === arr[1]) { - tok.escaped = true; - tok.val = arr[0]; - tok.segs = []; - return tok; - } - - if (arr.length > 1) { - if (opts.optimize !== false) { - opts.optimize = true; - delete opts.expand; - } - - if (opts.optimize !== true) { - var min = Math.min(arr[0], arr[1]); - var max = Math.max(arr[0], arr[1]); - var step = arr[2] || 1; - - if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) { - throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); - } - } - - arr.push(opts); - tok.segs = utils.fillRange.apply(null, arr); - - if (!tok.segs.length) { - tok.escaped = true; - tok.val = str; - return tok; - } - - if (opts.optimize === true) { - tok.segs = utils.stringifyArray(tok.segs); - } - - if (tok.segs === '') { - tok.val = str; - } else { - tok.val = tok.segs[0]; - } - return tok; - } - } else { - tok.val = str; + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; } - return tok; -}; - -/** - * Ensure commas inside brackets and parens are not split. - * @param {Object} `tok` Token from the `split-string` module - * @return {undefined} - */ - -utils.escapeBrackets = function(options) { - return function(tok) { - if (tok.escaped && tok.val === 'b') { - tok.val = '\\b'; - return; - } - - if (tok.val !== '(' && tok.val !== '[') return; - var opts = utils.extend({}, options); - var brackets = []; - var parens = []; - var stack = []; - var val = tok.val; - var str = tok.str; - var i = tok.idx - 1; - - while (++i < str.length) { - var ch = str[i]; - - if (ch === '\\') { - val += (opts.keepEscaping === false ? '' : ch) + str[++i]; - continue; - } - - if (ch === '(') { - parens.push(ch); - stack.push(ch); - } - - if (ch === '[') { - brackets.push(ch); - stack.push(ch); - } - - if (ch === ')') { - parens.pop(); - stack.pop(); - if (!stack.length) { - val += ch; - break; - } - } - - if (ch === ']') { - brackets.pop(); - stack.pop(); - if (!stack.length) { - val += ch; - break; - } - } - val += ch; - } - - tok.split = false; - tok.val = val.slice(1); - tok.idx = i; - }; -}; - -/** - * Returns true if the given string looks like a regex quantifier - * @return {Boolean} - */ - -utils.isQuantifier = function(str) { - return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str); -}; - -/** - * Cast `val` to an array. - * @param {*} `val` - */ - -utils.stringifyArray = function(arr) { - return [utils.arrayify(arr).join('|')]; + return false; }; /** - * Cast `val` to an array. - * @param {*} `val` + * Returns true if a node is an open or close node */ -utils.arrayify = function(arr) { - if (typeof arr === 'undefined') { - return []; - } - if (typeof arr === 'string') { - return [arr]; +exports.isOpenOrClose = node => { + if (node.type === 'open' || node.type === 'close') { + return true; } - return arr; + return node.open === true || node.close === true; }; /** - * Returns true if the given `str` is a non-empty string - * @return {Boolean} + * Reduce an array of text nodes. */ -utils.isString = function(str) { - return str != null && typeof str === 'string'; -}; +exports.reduce = nodes => nodes.reduce((acc, node) => { + if (node.type === 'text') acc.push(node.value); + if (node.type === 'range') node.type = 'text'; + return acc; +}, []); /** - * Get the last element from `array` - * @param {Array} `array` - * @return {*} + * Flatten an array */ -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; - -utils.escapeRegex = function(str) { - return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1'); +exports.flatten = (...args) => { + const result = []; + const flat = arr => { + for (let i = 0; i < arr.length; i++) { + let ele = arr[i]; + Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele); + } + return result; + }; + flat(args); + return result; }; diff --git a/package.json b/package.json index 96cbb2b..aeac709 100644 --- a/package.json +++ b/package.json @@ -22,42 +22,17 @@ ], "main": "index.js", "engines": { - "node": ">=0.10.0" + "node": ">=8" }, "scripts": { "test": "mocha", "benchmark": "node benchmark" }, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, "devDependencies": { - "ansi-cyan": "^0.1.1", - "benchmarked": "^2.0.0", - "brace-expansion": "^1.1.8", - "cross-spawn": "^5.1.0", - "gulp": "^3.9.1", - "gulp-eslint": "^4.0.0", - "gulp-format-md": "^1.0.0", - "gulp-istanbul": "^1.1.2", - "gulp-mocha": "^3.0.1", - "gulp-unused": "^0.2.1", - "is-windows": "^1.0.1", - "minimatch": "^3.0.4", - "mocha": "^3.2.0", - "noncharacters": "^1.1.0", - "text-table": "^0.2.0", - "time-diff": "^0.3.1", - "yargs-parser": "^8.0.0" + "ansi-colors": "^3.2.4", + "bash-path": "^2.0.1", + "gulp-format-md": "^2.0.0", + "mocha": "^6.0.2" }, "keywords": [ "alpha", @@ -104,5 +79,8 @@ "nanomatch" ] } + }, + "dependencies": { + "fill-range": "^6.0.0" } } diff --git a/test/bash-compiled-ranges.js b/test/bash-compiled-ranges.js new file mode 100644 index 0000000..02ac3bc --- /dev/null +++ b/test/bash-compiled-ranges.js @@ -0,0 +1,203 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); + +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.compile(input, options), expected); +}; + +/** + * Bash 4.3 unit tests with `braces.compile()` + */ + +describe('bash ranges - braces.compile()', () => { + const fixtures = [ + ['a{b,c{1..100}/{foo,bar}/,h}x/z', {}, 'a(b|c([1-9]|[1-9][0-9]|100)/(foo|bar)/|h)x/z'], + ['0{1..9} {10..20}', {}, '0([1-9]) (1[0-9]|20)'], + + // should not try to expand ranges with decimals + ['{1.1..2.1}', {}, '{1.1..2.1}'], + ['{1.1..~2.1}', {}, '{1.1..~2.1}'], + + // should escape invalid ranges + ['{1..0f}', {}, '{1..0f}'], + ['{1..10..ff}', {}, '{1..10..ff}'], + ['{1..10.f}', {}, '{1..10.f}'], + ['{1..10f}', {}, '{1..10f}'], + ['{1..20..2f}', {}, '{1..20..2f}'], + ['{1..20..f2}', {}, '{1..20..f2}'], + ['{1..2f..2}', {}, '{1..2f..2}'], + ['{1..ff..2}', {}, '{1..ff..2}'], + ['{1..ff}', {}, '{1..ff}'], + ['{1.20..2}', {}, '{1.20..2}'], + + // should handle weirdly-formed brace expansions (fixed in post-bash-3.1) + ['a-{b{d,e}}-c', {}, 'a-{b(d|e)}-c'], + ['a-{bdef-{g,i}-c', {}, 'a-{bdef-(g|i)-c'], + + // should not expand quoted strings + ['{"klklkl"}{1,2,3}', {}, '{klklkl}(1|2|3)'], + ['{"x,x"}', {}, '{x,x}'], + + // should escaped outer braces in nested non-sets + ['{a-{b,c,d}}', {}, '{a-(b|c|d)}'], + ['{a,{a-{b,c,d}}}', {}, '(a|{a-(b|c|d)})'], + + // should escape imbalanced braces + ['abc{', {}, 'abc{'], + ['{abc{', {}, '{abc{'], + ['{abc', {}, '{abc'], + ['}abc', {}, '}abc'], + ['ab{c', {}, 'ab{c'], + ['{{a,b}', {}, '{(a|b)'], + ['{a,b}}', {}, '(a|b)}'], + ['abcd{efgh', {}, 'abcd{efgh'], + ['a{b{c{d,e}f}gh', {}, 'a{b{c(d|e)f}gh'], + ['a{b{c{d,e}f}g}h', {}, 'a{b{c(d|e)f}g}h'], + ['f{x,y{{g,z}}h}', {}, 'f(x|y{(g|z)}h)'], + ['z{a,b},c}d', {}, 'z(a|b),c}d'], + ['a{b{c{d,e}f{x,y{{g}h', {}, 'a{b{c(d|e)f{x,y{{g}h'], + ['f{x,y{{g}h', {}, 'f{x,y{{g}h'], + ['f{x,y{{g}}h', {}, 'f{x,y{{g}}h'], + ['a{b{c{d,e}f{x,y{}g}h', {}, 'a{b{c(d|e)f(x|y{}g)h'], + ['f{x,y{}g}h', {}, 'f(x|y{}g)h'], + ['z{a,b{,c}d', {}, 'z{a,b(|c)d'], + + // should expand numeric ranges + ['a{0..3}d', {}, 'a([0-3])d'], + ['x{10..1}y', {}, 'x([1-9]|10)y'], + ['x{3..3}y', {}, 'x3y'], + ['{1..10}', {}, '([1-9]|10)'], + ['{1..3}', {}, '([1-3])'], + ['{1..9}', {}, '([1-9])'], + ['{10..1}', {}, '([1-9]|10)'], + ['{10..1}y', {}, '([1-9]|10)y'], + ['{3..3}', {}, '3'], + ['{5..8}', {}, '([5-8])'], + + // should expand ranges with negative numbers + ['{-10..-1}', {}, '(-[1-9]|-10)'], + ['{-20..0}', {}, '(-[1-9]|-1[0-9]|-20|0)'], + ['{0..-5}', {}, '(-[1-5]|0)'], + ['{9..-4}', {}, '(-[1-4]|[0-9])'], + + // should expand alphabetical ranges + ['0{1..9}/{10..20}', {}, '0([1-9])/(1[0-9]|20)'], + ['0{a..d}0', {}, '0([a-d])0'], + ['a/{b..d}/e', {}, 'a/([b-d])/e'], + ['{1..f}', {}, '([1-f])'], + ['{a..A}', {}, '([A-a])'], + ['{A..a}', {}, '([A-a])'], + ['{a..e}', {}, '([a-e])'], + ['{A..E}', {}, '([A-E])'], + ['{a..f}', {}, '([a-f])'], + ['{a..z}', {}, '([a-z])'], + ['{E..A}', {}, '([A-E])'], + ['{f..1}', {}, '([1-f])'], + ['{f..a}', {}, '([a-f])'], + ['{f..f}', {}, 'f'], + + // should expand multiple ranges + ['a/{b..d}/e/{f..h}', {}, 'a/([b-d])/e/([f-h])'], + + // should expand numerical ranges - positive and negative + ['{-10..10}', {}, '(-[1-9]|-?10|[0-9])'], + + // HEADS UP! If you're using the `--mm` flag minimatch freezes on these + // should expand large numbers + ['{2147483645..2147483649}', {}, '(214748364[5-9])'], + ['{214748364..2147483649}', {}, '(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])'], + + // should expand ranges using steps + ['{1..10..1}', { bash: false }, '([1-9]|10)'], + ['{1..10..2}', { bash: false }, '(1|3|5|7|9)'], + ['{1..20..20}', { bash: false }, '1'], + ['{1..20..2}', { bash: false }, '(1|3|5|7|9|11|13|15|17|19)'], + ['{10..1..2}', { bash: false }, '(10|8|6|4|2)'], + ['{100..0..5}', { bash: false }, '(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)'], + ['{2..10..1}', { bash: false }, '([2-9]|10)'], + ['{2..10..2}', { bash: false }, '(2|4|6|8|10)'], + ['{2..10..3}', { bash: false }, '(2|5|8)'], + + // should expand negative ranges using steps + ['{-1..-10..-2}', { bash: false }, '(-(1|3|5|7|9))'], + ['{-1..-10..2}', { bash: false }, '(-(1|3|5|7|9))'], + ['{-10..-2..2}', { bash: false }, '(-(10|8|6|4|2))'], + ['{-2..-10..1}', { bash: false }, '(-[2-9]|-10)'], + ['{-2..-10..2}', { bash: false }, '(-(2|4|6|8|10))'], + ['{-2..-10..3}', { bash: false }, '(-(2|5|8))'], + ['{-9..9..3}', { bash: false }, '(0|3|6|9|-(9|6|3))'], + ['{10..1..-2}', { bash: false }, '(10|8|6|4|2)'], + ['{100..0..-5}', { bash: false }, '(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)'], + + // should expand alpha ranges with steps + ['{a..e..2}', { bash: false }, '(a|c|e)'], + ['{E..A..2}', { bash: false }, '(E|C|A)'], + ['{a..z..2}', { bash: false }, '(a|c|e|g|i|k|m|o|q|s|u|w|y)'], + + // should expand alpha ranges with negative steps + ['{z..a..-2}', { bash: false }, '(z|x|v|t|r|p|n|l|j|h|f|d|b)'], + + // unwanted zero-padding (fixed post-bash-4.0) + ['{10..0..2}', { bash: false }, '(10|8|6|4|2|0)'], + ['{10..0..-2}', { bash: false }, '(10|8|6|4|2|0)'], + ['{-50..-0..5}', { bash: false }, '(0|-(50|45|40|35|30|25|20|15|10|5))'], + + // should work with dots in file paths + ['../{1..3}/../foo', {}, '../([1-3])/../foo'], + ['../{2..10..2}/../foo', {}, '../(2|4|6|8|10)/../foo'], + ['../{1..3}/../{a,b,c}/foo', {}, '../([1-3])/../(a|b|c)/foo'], + ['./{a..z..3}/', {}, './(a|d|g|j|m|p|s|v|y)/'], + ['./{"x,y"}/{a..z..3}/', { keepQuotes: true }, './{"x,y"}/(a|d|g|j|m|p|s|v|y)/'], + + // should expand a complex combination of ranges and sets + ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, 'a/(x|y)/([1-5])c(d|e)f.(md|txt)'], + + // should expand complex sets and ranges in `bash` mode + ['a/{x,{1..5},y}/c{d}e', {}, 'a/(x|([1-5])|y)/c{d}e'], + + // should treat glob characters as literal + ['**/{1..5}/a.js', {}, '**/([1-5])/a.js'], + ['x{{0..10},braces}y', {}, 'x(([0-9]|10)|braces)y'], + + // should handle sets with invalid ranges + ['{0..10,braces}', {}, '(0..10|braces)'], + ['{1..10,braces}', {}, '(1..10|braces)'], + + ['./\\{x,y}/{a..z..3}/', {}, './{x,y}/(a|d|g|j|m|p|s|v|y)/'], + ['{braces,{0..10}}', {}, '(braces|([0-9]|10))'], + ['{{0..10},braces}', {}, '(([0-9]|10)|braces)'], + ['{{1..10..2},braces}', { bash: false }, '((1|3|5|7|9)|braces)'], + ['{{1..10..2},braces}', {}, '((1|3|5|7|9)|braces)'] + ]; + + fixtures.forEach(arr => { + if (typeof arr === 'string') { + return; + } + + let options = { ...arr[1] }; + let pattern = arr[0]; + let expected = arr[2]; + + if (options.skip === true) { + return; + } + + it('should compile: ' + pattern, () => { + equal(pattern, expected, options); + }); + }); +}); + diff --git a/test/bash-compiled-sets.js b/test/bash-compiled-sets.js new file mode 100644 index 0000000..15bff41 --- /dev/null +++ b/test/bash-compiled-sets.js @@ -0,0 +1,199 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); + +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.compile(input, options), expected); +}; + +/** + * Bash 4.3 unit tests with `braces.expand()` + */ + +describe('bash sets - braces.compile()', () => { + const fixtures = [ + ['{a,b,c,d,e}', {}, '(a|b|c|d|e)'], + ['a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, 'a/{b,c,d,(x|y)}{e,f}/g'], + ['a/\\{b,c,d\\}\\{e,f\\}/g', {}, 'a/{b,c,d}{e,f}/g'], + ['a/\\{b,c,d\\}\\{e,f}/g', {}, 'a/{b,c,d}{e,f}/g'], + ['a/\\{b,c,d\\}{e,f}/g', {}, 'a/{b,c,d}(e|f)/g'], + ['a/\\{b,c,d{x,y}}{e,f\\}/g', {}, 'a/{b,c,d(x|y)}{e,f}/g'], + ['a/\\{b,c,d}{e,f\\}/g', {}, 'a/{b,c,d}{e,f}/g'], + ['a/\\{b,c,d}{e,f}/g', {}, 'a/{b,c,d}(e|f)/g'], + ['a/\\{{b,c}{e,f}/g', {}, 'a/{(b|c)(e|f)/g'], + ['a/\\{{b,c}{e,f}\\}/g', {}, 'a/{(b|c)(e|f)}/g'], + ['a/\\{{b,c}{e,f}}/g', {}, 'a/{(b|c)(e|f)}/g'], + ['a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, 'a/b/(b|c|(d|e(f|g)|(w|x)/(y|z)))/h/i'], + ['a/{b,c}}{e,f}/g', {}, 'a/(b|c)}(e|f)/g'], + ['a/{b,c\\,d}{e,f}/g', {}, 'a/(b|c,d)(e|f)/g'], + ['a/{b,c\\}}{e,f}/g', {}, 'a/(b|c})(e|f)/g'], + ['a/{b,c}', {}, 'a/(b|c)'], + ['a/{b,c}d{e,f}/g', {}, 'a/(b|c)d(e|f)/g'], + ['a/{b,c}{e,f}/g', {}, 'a/(b|c)(e|f)/g'], + ['a/{b,c}{e,f}{g,h,i}/k', {}, 'a/(b|c)(e|f)(g|h|i)/k'], + ['a/{b,{c,d},e}/f', {}, 'a/(b|(c|d)|e)/f'], + ['a/{b,{c,d}/{e,f},g}/h', {}, 'a/(b|(c|d)/(e|f)|g)/h'], + ['a/{b{c,d},e{f,g}h{i,j}}/k', {}, 'a/(b(c|d)|e(f|g)h(i|j))/k'], + ['a/{b{c,d},e}/f', {}, 'a/(b(c|d)|e)/f'], + ['a/{b{c,d}e{f,g}h{i,j}}/k', {}, 'a/{b(c|d)e(f|g)h(i|j)}/k'], + ['a/{b{c,d}e{f,g},h{i,j}}/k', {}, 'a/(b(c|d)e(f|g)|h(i|j))/k'], + ['a/{x,z}{b,{c,d}/{e,f},g}/h', {}, 'a/(x|z)(b|(c|d)/(e|f)|g)/h'], + ['a/{{a,b}/{c,d}}/z', {}, 'a/{(a|b)/(c|d)}/z'], + ['a/{{b,c}/{d,e}}', {}, 'a/{(b|c)/(d|e)}'], + ['a/{{b,c}/{d,e}}/f', {}, 'a/{(b|c)/(d|e)}/f'], + ['a{b{c{d,e}f{x,y{{g}h', {}, 'a{b{c(d|e)f{x,y{{g}h'], + ['{a,b,{c,d},e}', {}, '(a|b|(c|d)|e)'], + ['{a,b,{c,d}e}', {}, '(a|b|(c|d)e)'], + ['{a,b,{c,d}}', {}, '(a|b|(c|d))'], + ['{a,b{c,d}}', {}, '(a|b(c|d))'], + ['{a,b}/{c,d}', {}, '(a|b)/(c|d)'], + ['{a,b}{c,d}', {}, '(a|b)(c|d)'], + + ['{{a,b},{c,d}}', {}, '((a|b)|(c|d))'], + ['{{a,b}/{c,d}}', {}, '{(a|b)/(c|d)}'], + ['{{a,b}/{c,d}}/z', {}, '{(a|b)/(c|d)}/z'], + + // should not process glob characters + ['{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, '(generate|(assemble|update|verb)(file|-generate-*)|generator).js'], + ['**/{foo,bar}.js', {}, '**/(foo|bar).js'], + ['**/{a,b,c}/*.js', {}, '**/(a|b|c)/*.js'], + ['**/{a,b,*}/*.js', {}, '**/(a|b|*)/*.js'], + ['**/{**,b,*}/*.js', {}, '**/(**|b|*)/*.js'], + + // should not expand escaped braces + ['\\{a,b,c,d,e}', {}, '{a,b,c,d,e}'], + ['a/b/c/{x,y\\}', {}, 'a/b/c/{x,y}'], + ['a/\\{x,y}/cde', {}, 'a/{x,y}/cde'], + ['abcd{efgh', {}, 'abcd{efgh'], + ['\\{abc\\}', {}, '{abc}'], + ['{x,y,\\{a,b,c\\}}', {}, '(x|y|{a|b|c})'], + ['{x,y,{a,b,c\\}}', {}, '{x,y,(a|b|c})'], + ['{x,y,{abc},trie}', {}, '(x|y|{abc}|trie)'], + ['x,y,{abc},trie', {}, 'x,y,{abc},trie'], + ['{b{c,d},e}', {}, '(b(c|d)|e)'], + ['{b{c,d},e}/f', {}, '(b(c|d)|e)/f'], + ['{abc}', {}, '{abc}'], + + // should handle empty braces + ['{ }', {}, '{ }'], + ['{', {}, '{'], + ['{}', {}, '{}'], + ['}', {}, '}'], + + // should escape braces when only one value is defined + ['a{b}c', {}, 'a{b}c'], + ['a/b/c{d}e', {}, 'a/b/c{d}e'], + + // should escape closing braces when open is not defined + ['{a,b}c,d}', {}, '(a|b)c,d}'], + ['a,b,c,d}', {}, 'a,b,c,d}'], + + // should not expand braces in sets with es6/bash-like variables + ['abc/${ddd}/xyz', {}, 'abc/${ddd}/xyz'], + ['a${b}c', {}, 'a${b}c'], + ['a${b{a,b}}c', {}, 'a${b{a,b}}c'], + ['a/{${b},c}/d', {}, 'a/(${b}|c)/d'], + ['a${b,d}/{foo,bar}c', {}, 'a${b,d}/(foo|bar)c'], + + // should not expand escaped commas + ['a{b\\,c\\,d}e', {}, 'a{b,c,d}e'], + ['a{b\\,c}d', {}, 'a{b,c}d'], + ['{abc\\,def}', {}, '{abc,def}'], + ['{abc\\,def,ghi}', {}, '(abc,def|ghi)'], + ['a/{b,c}/{x\\,y}/d/e', {}, 'a/(b|c)/{x,y}/d/e'], + + // should not expand escaped braces + ['{a,b\\}c,d}', {}, '(a|b}c|d)'], + ['a/{z,\\{a,b,c,d,e}/d', {}, 'a/(z|{a|b|c|d|e)/d'], + ['a/\\{b,c}/{d,e}/f', {}, 'a/{b,c}/(d|e)/f'], + + // should not expand escaped braces or commas + ['{x\\,y,\\{abc\\},trie}', {}, '(x,y|{abc}|trie)'], + + // should support sequence brace operators + ['ff{c,b,a}', {}, 'ff(c|b|a)'], + ['f{d,e,f}g', {}, 'f(d|e|f)g'], + ['{a,b,c}', {}, '(a|b|c)'], + ['{l,n,m}xyz', {}, '(l|n|m)xyz'], + + // should expand multiple sets + ['a/{a,b}/{c,d}/e', {}, 'a/(a|b)/(c|d)/e'], + ['a{b,c}d{e,f}g', {}, 'a(b|c)d(e|f)g'], + ['a/{x,y}/c{d,e}f.{md,txt}', {}, 'a/(x|y)/c(d|e)f.(md|txt)'], + + // should expand nested sets + ['{a,b}{{a,b},a,b}', {}, '(a|b)((a|b)|a|b)'], + ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, '/usr/(ucb/(ex|edit)|lib/(ex|how_ex))'], + ['a{b,c{d,e}f}g', {}, 'a(b|c(d|e)f)g'], + ['a{{x,y},z}b', {}, 'a((x|y)|z)b'], + ['f{x,y{g,z}}h', {}, 'f(x|y(g|z))h'], + ['a{b,c{d,e},h}x/z', {}, 'a(b|c(d|e)|h)x/z'], + ['a{b,c{d,e},h}x{y,z}', {}, 'a(b|c(d|e)|h)x(y|z)'], + ['a{b,c{d,e},{f,g}h}x{y,z}', {}, 'a(b|c(d|e)|(f|g)h)x(y|z)'], + ['a-{b{d,e}}-c', {}, 'a-{b(d|e)}-c'], + + // should expand not modify non-brace characters + ['a/b/{d,e}/*.js', {}, 'a/b/(d|e)/*.js'], + ['a/**/c/{d,e}/f*.js', {}, 'a/**/c/(d|e)/f*.js'], + ['a/**/c/{d,e}/f*.{md,txt}', {}, 'a/**/c/(d|e)/f*.(md|txt)'], + + // should work with leading and trailing commas + ['a{b,}c', {}, 'a(b|)c'], + ['a{,b}c', {}, 'a(|b)c'], + + // should handle spaces + // Bash 4.3 says the this first one should be equivalent to `foo|(1|2)|bar + // That makes sense in Bash, since ' ' is a separator, but not here. + ['foo {1,2} bar', {}, 'foo (1|2) bar'], + ['a{ ,c{d, },h}x', {}, 'a( |c(d| )|h)x'], + ['a{ ,c{d, },h} ', {}, 'a( |c(d| )|h) '], + + // see https://github.com/jonschlinkert/microequal/issues/66 + ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)'] + ]; + + let seen = new Map(); + let dupes = []; + + for (let i = 0; i < fixtures.length; i++) { + let fixture = fixtures[i]; + + let key = fixture[0] + String(fixture[1].bash); + if (seen.has(key)) { + dupes.push(i + 21, fixture[0]); + } else { + + seen.set(key, i + 21); + } + } + + fixtures.forEach(arr => { + if (typeof arr === 'string') { + return; + } + + let options = { ...arr[1] }; + let pattern = arr[0]; + let expected = arr[2]; + + if (options.skip === true) { + return; + } + + it('should compile: ' + pattern, () => { + equal(pattern, expected, options); + }); + }); +}); + diff --git a/test/bash-expanded-ranges.js b/test/bash-expanded-ranges.js new file mode 100644 index 0000000..4524613 --- /dev/null +++ b/test/bash-expanded-ranges.js @@ -0,0 +1,317 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); + +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; + +/** + * Bash 4.3 unit tests with `braces.expand()` + */ + +describe('bash - expanded brace ranges', () => { + describe('large numbers', () => { + it('should expand large numbers', () => { + equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); + }); + + it('should throw an error when range exceeds rangeLimit', () => { + assert.throws(() => braces.expand('{214748364..2147483649}')); + }); + }); + + describe('escaping / invalid ranges', () => { + it('should not try to expand ranges with decimals', () => { + equal('{1.1..2.1}', ['{1.1..2.1}']); + equal('{1.1..~2.1}', ['{1.1..~2.1}']); + }); + + it('should escape invalid ranges:', () => { + equal('{1..0f}', ['{1..0f}']); + equal('{1..10..ff}', ['{1..10..ff}']); + equal('{1..10.f}', ['{1..10.f}']); + equal('{1..10f}', ['{1..10f}']); + equal('{1..20..2f}', ['{1..20..2f}']); + equal('{1..20..f2}', ['{1..20..f2}']); + equal('{1..2f..2}', ['{1..2f..2}']); + equal('{1..ff..2}', ['{1..ff..2}']); + equal('{1..ff}', ['{1..ff}']); + equal('{1.20..2}', ['{1.20..2}']); + }); + + it('weirdly-formed brace expansions -- fixed in post-bash-3.1', () => { + equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']); + equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); + }); + + it('should not expand quoted strings.', () => { + equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']); + equal('{"x,x"}', ['{x,x}']); + }); + + it('should escaped outer braces in nested non-sets', () => { + equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']); + equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']); + }); + + it('should escape imbalanced braces', () => { + equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); + equal('abc{', ['abc{']); + equal('{abc{', ['{abc{']); + equal('{abc', ['{abc']); + equal('}abc', ['}abc']); + equal('ab{c', ['ab{c']); + equal('ab{c', ['ab{c']); + equal('{{a,b}', ['{a', '{b']); + equal('{a,b}}', ['a}', 'b}']); + equal('abcd{efgh', ['abcd{efgh']); + equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']); + equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']); + equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']); + equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']); + equal('f{x,y{{g}h', ['f{x,y{{g}h']); + equal('f{x,y{{g}}h', ['f{x,y{{g}}h']); + equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']); + equal('f{x,y{}g}h', ['fxh', 'fy{}gh']); + equal('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']); + }); + }); + + describe('positive numeric ranges', () => { + it('should expand numeric ranges', () => { + equal('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']); + equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']); + equal('x{3..3}y', ['x3y']); + equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + equal('{1..3}', ['1', '2', '3']); + equal('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']); + equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']); + equal('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']); + equal('{3..3}', ['3']); + equal('{5..8}', ['5', '6', '7', '8']); + }); + }); + + describe('negative ranges', () => { + it('should expand ranges with negative numbers', () => { + equal('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']); + equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']); + equal('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']); + equal('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']); + }); + }); + + describe('alphabetical ranges', () => { + it('should expand alphabetical ranges', () => { + equal('{a..F}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F']); + equal('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']); + equal('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']); + equal('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']); + equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']); + equal('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']); + equal('{a..e}', ['a', 'b', 'c', 'd', 'e']); + equal('{A..E}', ['A', 'B', 'C', 'D', 'E']); + equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']); + equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']); + equal('{E..A}', ['E', 'D', 'C', 'B', 'A']); + equal('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']); + equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']); + equal('{f..f}', ['f']); + }); + + it('should expand multiple ranges:', () => { + equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']); + }); + }); + + describe('combo', () => { + it('should expand numerical ranges - positive and negative', () => { + equal('a{01..05}b', ['a01b', 'a02b', 'a03b', 'a04b', 'a05b' ]); + equal('0{1..9}/{10..20}', ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16', '05/17', '05/18', '05/19', '05/20', '06/10', '06/11', '06/12', '06/13', '06/14', '06/15', '06/16', '06/17', '06/18', '06/19', '06/20', '07/10', '07/11', '07/12', '07/13', '07/14', '07/15', '07/16', '07/17', '07/18', '07/19', '07/20', '08/10', '08/11', '08/12', '08/13', '08/14', '08/15', '08/16', '08/17', '08/18', '08/19', '08/20', '09/10', '09/11', '09/12', '09/13', '09/14', '09/15', '09/16', '09/17', '09/18', '09/19', '09/20' ]); + equal('{-10..10}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + }); + }); + + describe('steps > positive ranges', () => { + it('should expand ranges using steps:', () => { + equal('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + equal('{1..10..2}', ['1', '3', '5', '7', '9']); + equal('{1..20..20}', ['1']); + equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); + equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']); + equal('{10..1..2}', ['10', '8', '6', '4', '2']); + equal('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']); + equal('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']); + equal('{2..10..2}', ['2', '4', '6', '8', '10']); + equal('{2..10..3}', ['2', '5', '8']); + equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); + }); + + it('should expand positive ranges with negative steps:', () => { + equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']); + }); + }); + + describe('steps > negative ranges', () => { + it('should expand negative ranges using steps:', () => { + equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']); + equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']); + equal('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']); + equal('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); + equal('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']); + equal('{-2..-10..3}', ['-2', '-5', '-8']); + equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); + equal('{10..1..-2}', ['10', '8', '6', '4', '2']); + equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']); + }); + }); + + describe('steps > alphabetical ranges', () => { + it('should expand alpha ranges with steps', () => { + equal('{a..e..2}', ['a', 'c', 'e']); + equal('{E..A..2}', ['E', 'C', 'A']); + equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']); + equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); + equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); + }); + + it('should expand alpha ranges with negative steps', () => { + equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); + }); + }); + + describe('padding', () => { + it('unwanted zero-padding -- fixed post-bash-4.0', () => { + equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']); + equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']); + equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); + }); + }); + + describe('ranges', () => { + const fixtures = [ + 'should expand ranges', + + ['a{b,c{1..50}/{d,e,f}/,g}h/i', {}, ['abh/i', 'ac1/d/h/i', 'ac1/e/h/i', 'ac1/f/h/i', 'ac2/d/h/i', 'ac2/e/h/i', 'ac2/f/h/i', 'ac3/d/h/i', 'ac3/e/h/i', 'ac3/f/h/i', 'ac4/d/h/i', 'ac4/e/h/i', 'ac4/f/h/i', 'ac5/d/h/i', 'ac5/e/h/i', 'ac5/f/h/i', 'ac6/d/h/i', 'ac6/e/h/i', 'ac6/f/h/i', 'ac7/d/h/i', 'ac7/e/h/i', 'ac7/f/h/i', 'ac8/d/h/i', 'ac8/e/h/i', 'ac8/f/h/i', 'ac9/d/h/i', 'ac9/e/h/i', 'ac9/f/h/i', 'ac10/d/h/i', 'ac10/e/h/i', 'ac10/f/h/i', 'ac11/d/h/i', 'ac11/e/h/i', 'ac11/f/h/i', 'ac12/d/h/i', 'ac12/e/h/i', 'ac12/f/h/i', 'ac13/d/h/i', 'ac13/e/h/i', 'ac13/f/h/i', 'ac14/d/h/i', 'ac14/e/h/i', 'ac14/f/h/i', 'ac15/d/h/i', 'ac15/e/h/i', 'ac15/f/h/i', 'ac16/d/h/i', 'ac16/e/h/i', 'ac16/f/h/i', 'ac17/d/h/i', 'ac17/e/h/i', 'ac17/f/h/i', 'ac18/d/h/i', 'ac18/e/h/i', 'ac18/f/h/i', 'ac19/d/h/i', 'ac19/e/h/i', 'ac19/f/h/i', 'ac20/d/h/i', 'ac20/e/h/i', 'ac20/f/h/i', 'ac21/d/h/i', 'ac21/e/h/i', 'ac21/f/h/i', 'ac22/d/h/i', 'ac22/e/h/i', 'ac22/f/h/i', 'ac23/d/h/i', 'ac23/e/h/i', 'ac23/f/h/i', 'ac24/d/h/i', 'ac24/e/h/i', 'ac24/f/h/i', 'ac25/d/h/i', 'ac25/e/h/i', 'ac25/f/h/i', 'ac26/d/h/i', 'ac26/e/h/i', 'ac26/f/h/i', 'ac27/d/h/i', 'ac27/e/h/i', 'ac27/f/h/i', 'ac28/d/h/i', 'ac28/e/h/i', 'ac28/f/h/i', 'ac29/d/h/i', 'ac29/e/h/i', 'ac29/f/h/i', 'ac30/d/h/i', 'ac30/e/h/i', 'ac30/f/h/i', 'ac31/d/h/i', 'ac31/e/h/i', 'ac31/f/h/i', 'ac32/d/h/i', 'ac32/e/h/i', 'ac32/f/h/i', 'ac33/d/h/i', 'ac33/e/h/i', 'ac33/f/h/i', 'ac34/d/h/i', 'ac34/e/h/i', 'ac34/f/h/i', 'ac35/d/h/i', 'ac35/e/h/i', 'ac35/f/h/i', 'ac36/d/h/i', 'ac36/e/h/i', 'ac36/f/h/i', 'ac37/d/h/i', 'ac37/e/h/i', 'ac37/f/h/i', 'ac38/d/h/i', 'ac38/e/h/i', 'ac38/f/h/i', 'ac39/d/h/i', 'ac39/e/h/i', 'ac39/f/h/i', 'ac40/d/h/i', 'ac40/e/h/i', 'ac40/f/h/i', 'ac41/d/h/i', 'ac41/e/h/i', 'ac41/f/h/i', 'ac42/d/h/i', 'ac42/e/h/i', 'ac42/f/h/i', 'ac43/d/h/i', 'ac43/e/h/i', 'ac43/f/h/i', 'ac44/d/h/i', 'ac44/e/h/i', 'ac44/f/h/i', 'ac45/d/h/i', 'ac45/e/h/i', 'ac45/f/h/i', 'ac46/d/h/i', 'ac46/e/h/i', 'ac46/f/h/i', 'ac47/d/h/i', 'ac47/e/h/i', 'ac47/f/h/i', 'ac48/d/h/i', 'ac48/e/h/i', 'ac48/f/h/i', 'ac49/d/h/i', 'ac49/e/h/i', 'ac49/f/h/i', 'ac50/d/h/i', 'ac50/e/h/i', 'ac50/f/h/i', 'agh/i'] ], + ['0{1..9} {10..20}', {}, ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '05 17', '05 18', '05 19', '05 20', '06 10', '06 11', '06 12', '06 13', '06 14', '06 15', '06 16', '06 17', '06 18', '06 19', '06 20', '07 10', '07 11', '07 12', '07 13', '07 14', '07 15', '07 16', '07 17', '07 18', '07 19', '07 20', '08 10', '08 11', '08 12', '08 13', '08 14', '08 15', '08 16', '08 17', '08 18', '08 19', '08 20', '09 10', '09 11', '09 12', '09 13', '09 14', '09 15', '09 16', '09 17', '09 18', '09 19', '09 20'] ], + ['a{0..3}d', {}, ['a0d', 'a1d', 'a2d', 'a3d']], + ['x{10..1}y', {}, ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']], + ['x{3..3}y', {}, ['x3y']], + ['{0..10,braces}', {}, ['0..10', 'braces']], + ['{3..3}', {}, ['3']], + ['{5..8}', {}, ['5', '6', '7', '8']], + ['**/{1..5}/a.js', {}, ['**/1/a.js', '**/2/a.js', '**/3/a.js', '**/4/a.js', '**/5/a.js']], + ['{braces,{0..10}}', {}, ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']], + ['./\\{x,y}/{a..z..3}/', {}, ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/'] ], + ['x{{0..10},braces}y', {}, ['x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y', 'xbracesy'] ], + ['{braces,{0..10}}', {}, ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']], + ['{{0..10},braces}', {}, ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']], + ['{{1..10..2},braces}', {}, ['1', '3', '5', '7', '9', 'braces']], + ['{{1..10},braces}', {}, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']], + ['{1.1..2.1}', {}, ['{1.1..2.1}']], + ['{1.1..~2.1}', {}, ['{1.1..~2.1}']], + ['{1..0f}', {}, ['{1..0f}']], + ['{1..10..ff}', {}, ['{1..10..ff}']], + ['{1..10.f}', {}, ['{1..10.f}']], + ['{1..10f}', {}, ['{1..10f}']], + ['{1..20..2f}', {}, ['{1..20..2f}']], + ['{1..20..f2}', {}, ['{1..20..f2}']], + ['{1..2f..2}', {}, ['{1..2f..2}']], + ['{1..ff..2}', {}, ['{1..ff..2}']], + ['{1..ff}', {}, ['{1..ff}']], + ['{1.20..2}', {}, ['{1.20..2}']], + ['a{0..3}d', {}, ['a0d', 'a1d', 'a2d', 'a3d']], + ['x{10..1}y', {}, ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']], + ['x{3..3}y', {}, ['x3y']], + ['{1..10}', {}, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']], + ['{1..3}', {}, ['1', '2', '3']], + ['{1..9}', {}, ['1', '2', '3', '4', '5', '6', '7', '8', '9']], + ['{10..1}y', {}, ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']], + ['{3..3}', {}, ['3']], + ['{5..8}', {}, ['5', '6', '7', '8']], + ['{-10..-1}', {}, ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']], + ['{-20..0}', {}, ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0'] ], + ['{0..-5}', {}, ['0', '-1', '-2', '-3', '-4', '-5']], + ['{9..-4}', {}, ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']], + ['0{1..9}/{10..20}', {}, ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16', '05/17', '05/18', '05/19', '05/20', '06/10', '06/11', '06/12', '06/13', '06/14', '06/15', '06/16', '06/17', '06/18', '06/19', '06/20', '07/10', '07/11', '07/12', '07/13', '07/14', '07/15', '07/16', '07/17', '07/18', '07/19', '07/20', '08/10', '08/11', '08/12', '08/13', '08/14', '08/15', '08/16', '08/17', '08/18', '08/19', '08/20', '09/10', '09/11', '09/12', '09/13', '09/14', '09/15', '09/16', '09/17', '09/18', '09/19', '09/20'] ], + ['0{a..d}0', {}, ['0a0', '0b0', '0c0', '0d0']], + ['a/{b..d}/e', {}, ['a/b/e', 'a/c/e', 'a/d/e']], + ['{1..f}', { minimatch: false }, ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f'] ], + ['{a..A}', {}, ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A'] ], + ['{A..a}', {}, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a'] ], + ['{a..e}', {}, ['a', 'b', 'c', 'd', 'e']], + ['{A..E}', {}, ['A', 'B', 'C', 'D', 'E']], + ['{a..f}', {}, ['a', 'b', 'c', 'd', 'e', 'f']], + ['{a..z}', {}, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] ], + ['{E..A}', {}, ['E', 'D', 'C', 'B', 'A']], + ['{f..1}', { minimatch: false }, ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1'] ], + ['{f..a}', {}, ['f', 'e', 'd', 'c', 'b', 'a']], + ['{f..f}', {}, ['f']], + ['a/{b..d}/e/{f..h}', {}, ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h'] ], + ['{-10..10}', {}, ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] ], + ['{1..10..1}', { optimize: false }, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] ], + ['{1..10..2}', { optimize: false }, ['1', '3', '5', '7', '9'] ], + ['{1..20..20}', { optimize: false }, ['1'] ], + ['{1..20..2}', { optimize: false }, ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19'] ], + ['{10..0..2}', { optimize: false }, ['10', '8', '6', '4', '2', '0'] ], + ['{10..1..2}', { optimize: false }, ['10', '8', '6', '4', '2'] ], + ['{100..0..5}', { optimize: false }, ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0'] ], + ['{2..10..1}', { optimize: false }, ['2', '3', '4', '5', '6', '7', '8', '9', '10'] ], + ['{2..10..2}', { optimize: false }, ['2', '4', '6', '8', '10'] ], + ['{2..10..3}', { optimize: false }, ['2', '5', '8'] ], + ['{a..z..2}', { optimize: false }, ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y'] ], + ['{10..0..-2}', { optimize: false }, ['10', '8', '6', '4', '2', '0'] ], + ['{-1..-10..-2}', { optimize: false }, ['-1', '-3', '-5', '-7', '-9'] ], + ['{-1..-10..2}', { optimize: false }, ['-1', '-3', '-5', '-7', '-9'] ], + ['{-10..-2..2}', { optimize: false }, ['-10', '-8', '-6', '-4', '-2'] ], + ['{-2..-10..1}', { optimize: false }, ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10'] ], + ['{-2..-10..2}', { optimize: false }, ['-2', '-4', '-6', '-8', '-10'] ], + ['{-2..-10..3}', { optimize: false }, ['-2', '-5', '-8'] ], + ['{-50..-0..5}', { optimize: false }, ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0'] ], + ['{-9..9..3}', { optimize: false }, ['-9', '-6', '-3', '0', '3', '6', '9'] ], + ['{10..1..-2}', { optimize: false }, ['10', '8', '6', '4', '2'] ], + ['{100..0..-5}', { optimize: false }, ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0'] ], + ['{a..e..2}', { optimize: false }, ['a', 'c', 'e'] ], + ['{E..A..2}', { optimize: false }, ['E', 'C', 'A'] ], + ['{a..z..2}', { optimize: false }, ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y'] ], + ['{z..a..-2}', { optimize: false }, ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b'] ], + ['{z..a..-2}', { optimize: false }, ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b'] ], + ['{10..0..2}', { optimize: false }, ['10', '8', '6', '4', '2', '0'] ], + ['{10..0..-2}', { optimize: false }, ['10', '8', '6', '4', '2', '0'] ], + ['{-50..-0..5}', { optimize: false }, ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0'] ], + ['../{1..3}/../foo', {}, ['../1/../foo', '../2/../foo', '../3/../foo']], + ['../{2..10..2}/../foo', { optimize: false }, ['../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo'] ], + ['../{1..3}/../{a,b,c}/foo', {}, ['../1/../a/foo', '../1/../b/foo', '../1/../c/foo', '../2/../a/foo', '../2/../b/foo', '../2/../c/foo', '../3/../a/foo', '../3/../b/foo', '../3/../c/foo'] ], + ['./{a..z..3}/', { optimize: false }, ['./a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/'] ], + ['./{"x,y"}/{a..z..3}/', { minimatch: false, optimize: false }, ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/'] ], + ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, ['a/x/1cdf.md', 'a/x/1cdf.txt', 'a/x/1cef.md', 'a/x/1cef.txt', 'a/x/2cdf.md', 'a/x/2cdf.txt', 'a/x/2cef.md', 'a/x/2cef.txt', 'a/x/3cdf.md', 'a/x/3cdf.txt', 'a/x/3cef.md', 'a/x/3cef.txt', 'a/x/4cdf.md', 'a/x/4cdf.txt', 'a/x/4cef.md', 'a/x/4cef.txt', 'a/x/5cdf.md', 'a/x/5cdf.txt', 'a/x/5cef.md', 'a/x/5cef.txt', 'a/y/1cdf.md', 'a/y/1cdf.txt', 'a/y/1cef.md', 'a/y/1cef.txt', 'a/y/2cdf.md', 'a/y/2cdf.txt', 'a/y/2cef.md', 'a/y/2cef.txt', 'a/y/3cdf.md', 'a/y/3cdf.txt', 'a/y/3cef.md', 'a/y/3cef.txt', 'a/y/4cdf.md', 'a/y/4cdf.txt', 'a/y/4cef.md', 'a/y/4cef.txt', 'a/y/5cdf.md', 'a/y/5cdf.txt', 'a/y/5cef.md', 'a/y/5cef.txt']], + ['a/{x,{1..5},y}/c{d}e', {}, ['a/x/c{d}e', 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/y/c{d}e']] + ]; + + fixtures.forEach(arr => { + if (typeof arr === 'string') { + return; + } + + let options = { ...arr[1] }; + let pattern = arr[0]; + let expected = arr[2]; + + if (options.skip !== true) { + it('should compile: ' + pattern, () => equal(pattern, expected, options)); + } + }); + }); +}); diff --git a/test/bash-expanded-sets.js b/test/bash-expanded-sets.js new file mode 100644 index 0000000..718ac40 --- /dev/null +++ b/test/bash-expanded-sets.js @@ -0,0 +1,241 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); + +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; + +/** + * Bash 4.3 unit tests with `braces.expand()` + */ + +describe('bash - expanded brace sets', () => { + const fixtures = [ + [ 'a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,d,x}{e,f}/g', 'a/{b,c,d,y}{e,f}/g' ] ], + [ 'a/\\{b,c,d\\}\\{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], + [ 'a/\\{b,c,d\\}\\{e,f}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], + [ 'a/\\{b,c,d\\}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ], + [ 'a/\\{b,c,d{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,dx}{e,f}/g', 'a/{b,c,dy}{e,f}/g' ] ], + [ 'a/\\{b,c,d}{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], + [ 'a/\\{b,c,d}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ], + [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ], + [ 'a/\\{{b,c}{e,f}/g', {}, [ 'a/{be/g', 'a/{bf/g', 'a/{ce/g', 'a/{cf/g' ] ], + [ 'a/\\{{b,c}{e,f}\\}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ], + [ 'a/\\{{b,c}{e,f}}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ], + [ 'a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, [ 'a/b/b/h/i', 'a/b/c/h/i', 'a/b/d/h/i', 'a/b/ef/h/i', 'a/b/eg/h/i', 'a/b/w/y/h/i', 'a/b/w/z/h/i', 'a/b/x/y/h/i', 'a/b/x/z/h/i' ] ], + [ 'a/{b,c,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g', 'a/de/g', 'a/df/g' ] ], + [ 'a/{b,c\\,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c,de/g', 'a/c,df/g' ] ], + [ 'a/{b,c\\}}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c}e/g', 'a/c}f/g' ] ], + [ 'a/{b,c}', {}, [ 'a/b', 'a/c' ] ], + [ 'a/{b,c}d{e,f}/g', {}, [ 'a/bde/g', 'a/bdf/g', 'a/cde/g', 'a/cdf/g' ] ], + [ 'a/{b,c}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g' ] ], + [ 'a/{b,c}{e,f}{g,h,i}/k', {}, [ 'a/beg/k', 'a/beh/k', 'a/bei/k', 'a/bfg/k', 'a/bfh/k', 'a/bfi/k', 'a/ceg/k', 'a/ceh/k', 'a/cei/k', 'a/cfg/k', 'a/cfh/k', 'a/cfi/k' ] ], + [ 'a/{b,{c,d},e}/f', {}, [ 'a/b/f', 'a/c/f', 'a/d/f', 'a/e/f' ] ], + [ 'a/{b,{c,d}/{e,f},g}/h', {}, [ 'a/b/h', 'a/c/e/h', 'a/c/f/h', 'a/d/e/h', 'a/d/f/h', 'a/g/h' ] ], + [ 'a/{b{c,d},e{f,g}h{i,j}}/k', {}, [ 'a/bc/k', 'a/bd/k', 'a/efhi/k', 'a/efhj/k', 'a/eghi/k', 'a/eghj/k' ] ], + [ 'a/{b{c,d},e}/f', {}, [ 'a/bc/f', 'a/bd/f', 'a/e/f' ] ], + [ 'a/{b{c,d}e{f,g}h{i,j}}/k', {}, [ 'a/{bcefhi}/k', 'a/{bcefhj}/k', 'a/{bceghi}/k', 'a/{bceghj}/k', 'a/{bdefhi}/k', 'a/{bdefhj}/k', 'a/{bdeghi}/k', 'a/{bdeghj}/k' ] ], + [ 'a/{b{c,d}e{f,g},h{i,j}}/k', {}, [ 'a/bcef/k', 'a/bceg/k', 'a/bdef/k', 'a/bdeg/k', 'a/hi/k', 'a/hj/k' ] ], + [ 'a/{x,z}{b,{c,d}/{e,f},g}/h', {}, [ 'a/xb/h', 'a/xc/e/h', 'a/xc/f/h', 'a/xd/e/h', 'a/xd/f/h', 'a/xg/h', 'a/zb/h', 'a/zc/e/h', 'a/zc/f/h', 'a/zd/e/h', 'a/zd/f/h', 'a/zg/h' ] ], + [ 'a/{{a,b}/{c,d}}/z', {}, [ 'a/{a/c}/z', 'a/{a/d}/z', 'a/{b/c}/z', 'a/{b/d}/z' ] ], + [ 'a/{{b,c}/{d,e}}', {}, [ 'a/{b/d}', 'a/{b/e}', 'a/{c/d}', 'a/{c/e}' ] ], + [ 'a/{{b,c}/{d,e}}/f', {}, [ 'a/{b/d}/f', 'a/{b/e}/f', 'a/{c/d}/f', 'a/{c/e}/f' ] ], + [ 'a{b}c', {}, [ 'a{b}c' ] ], + [ 'foo {1,2} bar', {}, [ 'foo 1 bar', 'foo 2 bar' ] ], + [ '{ }', {}, [ '{ }' ] ], + [ '{', {}, [ '{' ] ], + [ '{a,b,{c,d},e}', {}, [ 'a', 'b', 'c', 'd', 'e' ] ], + [ '{a,b,{c,d}e}', {}, [ 'a', 'b', 'ce', 'de' ] ], + [ '{a,b,{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ], + [ '{a,b{c,d}}', {}, [ 'a', 'bc', 'bd' ] ], + [ '{a,b}/{c,d}', {}, [ 'a/c', 'a/d', 'b/c', 'b/d' ] ], + [ '{a,b}c,d\\}', {}, [ 'ac,d}', 'bc,d}' ] ], + [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ], + [ '{a,b}{c,d}', {}, [ 'ac', 'ad', 'bc', 'bd' ] ], + [ '{abc}', {}, [ '{abc}' ] ], + [ '{b{c,d},e}', {}, [ 'bc', 'bd', 'e' ] ], + [ '{b{c,d},e}/f', {}, [ 'bc/f', 'bd/f', 'e/f' ] ], + [ 'x,y,{abc},trie', {}, [ 'x,y,{abc},trie' ] ], + [ '{{a,b},{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ], + [ '{{a,b}/{c,d}}', {}, [ '{a/c}', '{a/d}', '{b/c}', '{b/d}' ] ], + [ '{{a,b}/{c,d}}/z', {}, [ '{a/c}/z', '{a/d}/z', '{b/c}/z', '{b/d}/z' ] ], + [ '{}', {}, [ '{}' ] ], + + // // should ignore globs + [ '}', {}, [ '}' ] ], + + // 'should ignore globs', + + [ '{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, [ 'generate.js', 'assemblefile.js', 'assemble-generate-*.js', 'updatefile.js', 'update-generate-*.js', 'verbfile.js', 'verb-generate-*.js', 'generator.js' ] ], + [ '**/{foo,bar}.js', {}, [ '**/foo.js', '**/bar.js' ] ], + [ '**/{a,b,c}/*.js', {}, [ '**/a/*.js', '**/b/*.js', '**/c/*.js' ] ], + [ '**/{a,b,*}/*.js', {}, [ '**/a/*.js', '**/b/*.js', '**/*/*.js' ] ], + [ '**/{**,b,*}/*.js', {}, [ '**/**/*.js', '**/b/*.js', '**/*/*.js' ] ], + [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/ucb/ex', '/usr/ucb/edit', '/usr/lib/ex', '/usr/lib/how_ex' ] ], + [ 'ff{c,b,a}', {}, [ 'ffc', 'ffb', 'ffa' ] ], + [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ], + [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ], + [ '{l,m,n}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ], + [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ], + [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ], + [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ], + [ '{a,b}{{a,b},a,b}', {}, [ 'aa', 'ab', 'aa', 'ab', 'ba', 'bb', 'ba', 'bb' ] ], + [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ], + [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ], + [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ], + [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ], + [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ], + [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ], + + // 'should not expand escaped braces', + + [ '\\{a,b,c,d,e}', {}, [ '{a,b,c,d,e}' ] ], + [ 'a/\\{b,c}/{d,e}/f', {}, [ 'a/{b,c}/d/f', 'a/{b,c}/e/f' ] ], + [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ], + [ 'a/b/c/{x,y\\}', {}, [ 'a/b/c/{x,y}' ] ], + [ 'a/{z,\\{a,b,c,d,e}/d', {}, [ 'a/z/d', 'a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d' ] ], + [ 'abcd{efgh', {}, [ 'abcd{efgh' ] ], + [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ], + [ '{abc}', {}, [ '{abc}' ] ], + [ '{x,y,\\{a,b,c\\}}', {}, [ 'x', 'y', '{a', 'b', 'c}' ] ], + [ '{x,y,{abc},trie}', {}, [ 'x', 'y', '{abc}', 'trie' ] ], + [ '{x,y,{a,b,c\\}}', {}, [ '{x,y,a', '{x,y,b', '{x,y,c}' ] ], + + 'should not expand escaped commas', + + [ '{x\\,y,\\{abc\\},trie}', {}, [ 'x,y', '{abc}', 'trie' ] ], + [ 'a{b\\,c\\,d}e', {}, [ 'a{b,c,d}e' ] ], + [ 'a{b\\,c}d', {}, [ 'a{b,c}d' ] ], + [ '{abc\\,def}', {}, [ '{abc,def}' ] ], + [ '{abc\\,def,ghi}', {}, [ 'abc,def', 'ghi' ] ], + [ 'a/{b,c}/{x\\,y}/d/e', {}, [ 'a/b/{x,y}/d/e', 'a/c/{x,y}/d/e' ] ], + + 'should handle empty braces', + + [ '{ }', {}, [ '{ }' ] ], + [ '{', {}, [ '{' ] ], + [ '{}', {}, [ '{}' ] ], + [ '}', {}, [ '}' ] ], + + 'should escape braces when only one value is defined', + + [ 'a{b}c', {}, [ 'a{b}c' ] ], + [ 'a/b/c{d}e', {}, [ 'a/b/c{d}e' ] ], + + 'should escape closing braces when open is not defined', + + [ '{a,b}c,d}', {}, [ 'ac,d}', 'bc,d}' ] ], + + 'should not expand braces in sets with es6/bash-like variables', + + [ 'abc/${ddd}/xyz', {}, [ 'abc/${ddd}/xyz' ] ], + [ 'a${b}c', {}, [ 'a${b}c' ] ], + [ 'a/{${b},c}/d', {}, [ 'a/${b}/d', 'a/c/d' ] ], + [ 'a${b,d}/{foo,bar}c', {}, [ 'a${b,d}/fooc', 'a${b,d}/barc' ] ], + + 'should support sequence brace operators', + + [ 'ff{a,b,c}', {}, [ 'ffa', 'ffb', 'ffc' ] ], + [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ], + [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ], + [ '{l,m,n}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ], + + 'should expand multiple sets', + + [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ], + [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ], + [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ], + + 'should expand nested sets', + + [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ], + [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ], + [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ], + [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ], + [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ], + [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ], + [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ], + + 'should do nothing to glob characters', + + [ 'a/b/{d,e}/*.js', {}, [ 'a/b/d/*.js', 'a/b/e/*.js' ] ], + [ 'a/**/c/{d,e}/f*.js', {}, [ 'a/**/c/d/f*.js', 'a/**/c/e/f*.js' ] ], + [ 'a/**/c/{d,e}/f*.{md,txt}', {}, [ 'a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt' ] ], + [ 'a/b/{d,e,[1-5]}/*.js', {}, [ 'a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js' ] ], + + 'should work with leading and trailing commas', + [ 'a{b,}c', {}, [ 'abc', 'ac' ] ], + [ 'a{,b}c', {}, [ 'ac', 'abc' ] ], + + 'should handle spaces', + [ 'a{ ,c{d, },h}x', {}, [ 'a x', 'acdx', 'ac x', 'ahx' ] ], + [ 'a{ ,c{d, },h} ', {}, [ 'a ', 'acd ', 'ac ', 'ah ' ] ], + + 'see https://github.com/jonschlinkert/microequal/issues/66', + [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs' ] ], + + 'should handle weirdly-formed brace expansions (fixed in post-bash-3.1)', + + [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ], + [ 'a-{bdef-{g,i}-c', {}, [ 'a-{bdef-g-c', 'a-{bdef-i-c' ] ], + + // 'should not expand quoted strings', + + [ '{"foo"}{1,2,3}', {}, [ '{foo}1', '{foo}2', '{foo}3' ] ], + [ '{"foo"}{1,2,3}', { keepQuotes: true }, [ '{"foo"}1', '{"foo"}2', '{"foo"}3' ] ], + [ '{"x,x"}', { keepQuotes: true }, [ '{"x,x"}' ] ], + [ '{\'x,x\'}', { keepQuotes: true }, [ '{\'x,x\'}' ] ], + + 'should escape outer braces in nested non-sets', + + [ '{a-{b,c,d}}', {}, [ '{a-b}', '{a-c}', '{a-d}' ] ], + [ '{a,{a-{b,c,d}}}', {}, [ 'a', '{a-b}', '{a-c}', '{a-d}' ] ], + + 'should escape imbalanced braces', + + [ 'abc{', {}, [ 'abc{' ] ], + [ '{abc{', {}, [ '{abc{' ] ], + [ '{abc', {}, [ '{abc' ] ], + [ '}abc', {}, [ '}abc' ] ], + [ 'ab{c', {}, [ 'ab{c' ] ], + [ 'ab{c', {}, [ 'ab{c' ] ], + [ '{{a,b}', {}, [ '{a', '{b' ] ], + [ '{a,b}}', {}, [ 'a}', 'b}' ] ], + [ 'a{b{c{d,e}f}gh', {}, [ 'a{b{cdf}gh', 'a{b{cef}gh' ] ], + [ 'a{b{c{d,e}f}g}h', {}, [ 'a{b{cdf}g}h', 'a{b{cef}g}h' ] ], + [ 'f{x,y{{g,z}}h}', {}, [ 'fx', 'fy{g}h', 'fy{z}h' ] ], + [ 'z{a,b},c}d', {}, [ 'za,c}d', 'zb,c}d' ] ], + [ 'a{b{c{d,e}f{x,y{{g}h', {}, [ 'a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h' ] ], + [ 'f{x,y{{g}h', {}, [ 'f{x,y{{g}h' ] ], + [ 'f{x,y{{g}}h', {}, [ 'f{x,y{{g}}h' ] ], + [ 'a{b{c{d,e}f{x,y{}g}h', {}, [ 'a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh' ] ], + [ 'f{x,y{}g}h', {}, [ 'fxh', 'fy{}gh' ] ], + [ 'z{a,b{,c}d', {}, [ 'z{a,bd', 'z{a,bcd' ] ] + ]; + + fixtures.forEach(arr => { + if (typeof arr === 'string') { + return; + } + + let options = { ...arr[1] }; + let pattern = arr[0]; + let expected = arr[2]; + + if (options.skip !== true) { + it('should compile: ' + pattern, () => equal(pattern, expected, options)); + } + }); +}); diff --git a/test/bash.spec.js b/test/bash-spec.js similarity index 93% rename from test/bash.spec.js rename to test/bash-spec.js index bf22ab4..3cb6f6f 100644 --- a/test/bash.spec.js +++ b/test/bash-spec.js @@ -1,34 +1,42 @@ 'use strict'; -var extend = require('extend-shallow'); -var assert = require('assert'); -var braces = require('..'); +require('mocha'); +const assert = require('assert').strict; +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; /** * Bash 4.3 unit tests */ -describe('bash', function() { +describe('bash', () => { var fixtures = [ [ '{1\\.2}', {}, [ '{1.2}' ] ], - [ '{1\\.2}', {unescape: false}, [ '{1\\.2}' ] ], + [ '{1\\.2}', { keepEscaping: true }, [ '{1\\.2}' ] ], [ '{"x,x"}', {}, [ '{x,x}' ] ], [ '{x","x}', {}, [ '{x,x}' ] ], [ '\'{x,x}\'', {}, [ '{x,x}' ] ], [ '{x`,`x}', {}, [ '{x,x}' ] ], - [ '{x`,`x}', {unescape: false}, [ '{x`,`x}' ] ], + [ '{x`,`x}', { keepQuotes: true }, [ '{x`,`x}' ] ], [ '\'{a,b}{{a,b},a,b}\'', {}, [ '{a,b}{{a,b},a,b}' ] ], [ 'A{b,{d,e},{f,g}}Z', {}, [ 'AbZ', 'AdZ', 'AeZ', 'AfZ', 'AgZ' ] ], [ 'PRE-{a,b}{{a,b},a,b}-POST', {}, [ 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-ba-POST', 'PRE-bb-POST', 'PRE-ba-POST', 'PRE-bb-POST' ] ], [ '\\{a,b}{{a,b},a,b}', {}, [ '{a,b}a', '{a,b}b', '{a,b}a', '{a,b}b' ] ], [ '{{a,b}', {}, [ '{a', '{b' ] ], [ '{a,b}}', {}, [ 'a}', 'b}' ] ], - [ '{,}', {}, [] ], + [ '{,}', {}, ['', ''] ], [ 'a{,}', {}, [ 'a', 'a' ] ], [ '{,}b', {}, [ 'b', 'b' ] ], [ 'a{,}b', {}, [ 'ab', 'ab' ] ], @@ -90,15 +98,14 @@ describe('bash', function() { [ '{{a,b}', {}, [ '{a', '{b' ] ], [ '{{a,b},c}', {}, [ 'a', 'b', 'c' ] ], [ '{{a,b}c}', {}, [ '{ac}', '{bc}' ] ], - [ '{{a,b},}', {}, [ '', 'a', 'b' ] ], + [ '{{a,b},}', {}, [ 'a', 'b', '' ] ], [ 'X{{a,b},}X', {}, [ 'XaX', 'XbX', 'XX' ] ], [ '{{a,b},}c', {}, [ 'ac', 'bc', 'c' ] ], [ '{{a,b}.}', {}, [ '{a.}', '{b.}' ] ], [ '{{a,b}}', {}, [ '{a}', '{b}' ] ], [ 'X{a..#}X', {}, [ 'X{a..#}X' ] ], - [ '', {}, [ '' ] ], [ '{-10..00}', {}, [ '-10', '-09', '-08', '-07', '-06', '-05', '-04', '-03', '-02', '-01', '000' ] ], - [ '{a,\\\\{a,b}c}', {}, [ 'a', '\\\\ac', '\\\\bc' ] ], + [ '{a,\\\\{a,b}c}', {}, [ 'a', '\\ac', '\\bc' ] ], [ '{a,\\{a,b}c}', {}, [ 'ac}', '{ac}', 'bc}' ] ], [ 'a,\\{b,c}', {}, [ 'a,{b,c}' ] ], [ '{-10.\\.00}', {}, [ '{-10..00}' ] ], @@ -170,19 +177,20 @@ describe('bash', function() { [ '{}a,b}c', {}, [ '{}a,b}c' ] ] ]; - fixtures.forEach(function(arr) { + fixtures.forEach(arr => { if (typeof arr === 'string') { return; } - var options = extend({}, arr[1]); - var pattern = arr[0]; - var expected = arr[2]; + let options = { ...arr[1] }; + let pattern = arr[0]; + let expected = arr[2]; + if (options.skip === true) { return; } - it('should compile: ' + pattern, function() { + it('should compile: ' + pattern, () => { equal(pattern, expected, options); }); }); diff --git a/test/bash.expanded.js b/test/bash.expanded.js deleted file mode 100644 index f2a4951..0000000 --- a/test/bash.expanded.js +++ /dev/null @@ -1,417 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options); - assert.deepEqual(actual.sort(), expected.sort(), pattern); -} - -/** - * Bash 4.3 unit tests with `braces.expand()` - */ - -describe('bash.expanded', function() { - it('should throw an error when range exceeds rangeLimit', function() { - assert.throws(function() { - braces.expand('{214748364..2147483649}'); - }); - }); - - var fixtures = [ - [ 'a{b,c{1..50}/{foo,bar,baz}/,g}h/i', {}, [ 'abh/i', 'ac1/bar/h/i', 'ac1/baz/h/i', 'ac1/foo/h/i', 'ac10/bar/h/i', 'ac10/baz/h/i', 'ac10/foo/h/i', 'ac11/bar/h/i', 'ac11/baz/h/i', 'ac11/foo/h/i', 'ac12/bar/h/i', 'ac12/baz/h/i', 'ac12/foo/h/i', 'ac13/bar/h/i', 'ac13/baz/h/i', 'ac13/foo/h/i', 'ac14/bar/h/i', 'ac14/baz/h/i', 'ac14/foo/h/i', 'ac15/bar/h/i', 'ac15/baz/h/i', 'ac15/foo/h/i', 'ac16/bar/h/i', 'ac16/baz/h/i', 'ac16/foo/h/i', 'ac17/bar/h/i', 'ac17/baz/h/i', 'ac17/foo/h/i', 'ac18/bar/h/i', 'ac18/baz/h/i', 'ac18/foo/h/i', 'ac19/bar/h/i', 'ac19/baz/h/i', 'ac19/foo/h/i', 'ac2/bar/h/i', 'ac2/baz/h/i', 'ac2/foo/h/i', 'ac20/bar/h/i', 'ac20/baz/h/i', 'ac20/foo/h/i', 'ac21/bar/h/i', 'ac21/baz/h/i', 'ac21/foo/h/i', 'ac22/bar/h/i', 'ac22/baz/h/i', 'ac22/foo/h/i', 'ac23/bar/h/i', 'ac23/baz/h/i', 'ac23/foo/h/i', 'ac24/bar/h/i', 'ac24/baz/h/i', 'ac24/foo/h/i', 'ac25/bar/h/i', 'ac25/baz/h/i', 'ac25/foo/h/i', 'ac26/bar/h/i', 'ac26/baz/h/i', 'ac26/foo/h/i', 'ac27/bar/h/i', 'ac27/baz/h/i', 'ac27/foo/h/i', 'ac28/bar/h/i', 'ac28/baz/h/i', 'ac28/foo/h/i', 'ac29/bar/h/i', 'ac29/baz/h/i', 'ac29/foo/h/i', 'ac3/bar/h/i', 'ac3/baz/h/i', 'ac3/foo/h/i', 'ac30/bar/h/i', 'ac30/baz/h/i', 'ac30/foo/h/i', 'ac31/bar/h/i', 'ac31/baz/h/i', 'ac31/foo/h/i', 'ac32/bar/h/i', 'ac32/baz/h/i', 'ac32/foo/h/i', 'ac33/bar/h/i', 'ac33/baz/h/i', 'ac33/foo/h/i', 'ac34/bar/h/i', 'ac34/baz/h/i', 'ac34/foo/h/i', 'ac35/bar/h/i', 'ac35/baz/h/i', 'ac35/foo/h/i', 'ac36/bar/h/i', 'ac36/baz/h/i', 'ac36/foo/h/i', 'ac37/bar/h/i', 'ac37/baz/h/i', 'ac37/foo/h/i', 'ac38/bar/h/i', 'ac38/baz/h/i', 'ac38/foo/h/i', 'ac39/bar/h/i', 'ac39/baz/h/i', 'ac39/foo/h/i', 'ac4/bar/h/i', 'ac4/baz/h/i', 'ac4/foo/h/i', 'ac40/bar/h/i', 'ac40/baz/h/i', 'ac40/foo/h/i', 'ac41/bar/h/i', 'ac41/baz/h/i', 'ac41/foo/h/i', 'ac42/bar/h/i', 'ac42/baz/h/i', 'ac42/foo/h/i', 'ac43/bar/h/i', 'ac43/baz/h/i', 'ac43/foo/h/i', 'ac44/bar/h/i', 'ac44/baz/h/i', 'ac44/foo/h/i', 'ac45/bar/h/i', 'ac45/baz/h/i', 'ac45/foo/h/i', 'ac46/bar/h/i', 'ac46/baz/h/i', 'ac46/foo/h/i', 'ac47/bar/h/i', 'ac47/baz/h/i', 'ac47/foo/h/i', 'ac48/bar/h/i', 'ac48/baz/h/i', 'ac48/foo/h/i', 'ac49/bar/h/i', 'ac49/baz/h/i', 'ac49/foo/h/i', 'ac5/bar/h/i', 'ac5/baz/h/i', 'ac5/foo/h/i', 'ac50/bar/h/i', 'ac50/baz/h/i', 'ac50/foo/h/i', 'ac6/bar/h/i', 'ac6/baz/h/i', 'ac6/foo/h/i', 'ac7/bar/h/i', 'ac7/baz/h/i', 'ac7/foo/h/i', 'ac8/bar/h/i', 'ac8/baz/h/i', 'ac8/foo/h/i', 'ac9/bar/h/i', 'ac9/baz/h/i', 'ac9/foo/h/i', 'agh/i' ] ], - [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '05 17', '05 18', '05 19', '05 20', '06 10', '06 11', '06 12', '06 13', '06 14', '06 15', '06 16', '06 17', '06 18', '06 19', '06 20', '07 10', '07 11', '07 12', '07 13', '07 14', '07 15', '07 16', '07 17', '07 18', '07 19', '07 20', '08 10', '08 11', '08 12', '08 13', '08 14', '08 15', '08 16', '08 17', '08 18', '08 19', '08 20', '09 10', '09 11', '09 12', '09 13', '09 14', '09 15', '09 16', '09 17', '09 18', '09 19', '09 20' ] ], - [ 'a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,d,x}{e,f}/g', 'a/{b,c,d,y}{e,f}/g' ] ], - [ 'a/\\{b,c,d\\}\\{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], - [ 'a/\\{b,c,d\\}\\{e,f}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], - [ 'a/\\{b,c,d\\}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ], - [ 'a/\\{b,c,d{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,dx}{e,f}/g', 'a/{b,c,dy}{e,f}/g' ] ], - [ 'a/\\{b,c,d}{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ], - [ 'a/\\{b,c,d}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ], - [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ], - [ 'a/\\{{b,c}{e,f}/g', {}, [ 'a/{be/g', 'a/{bf/g', 'a/{ce/g', 'a/{cf/g' ] ], - [ 'a/\\{{b,c}{e,f}\\}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ], - [ 'a/\\{{b,c}{e,f}}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ], - [ 'a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, [ 'a/b/b/h/i', 'a/b/c/h/i', 'a/b/d/h/i', 'a/b/ef/h/i', 'a/b/eg/h/i', 'a/b/w/y/h/i', 'a/b/w/z/h/i', 'a/b/x/y/h/i', 'a/b/x/z/h/i' ] ], - [ 'a/{b,c,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g', 'a/de/g', 'a/df/g' ] ], - [ 'a/{b,c\\,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c,de/g', 'a/c,df/g' ] ], - [ 'a/{b,c\\}}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c}e/g', 'a/c}f/g' ] ], - [ 'a/{b,c}', {}, [ 'a/b', 'a/c' ] ], - [ 'a/{b,c}d{e,f}/g', {}, [ 'a/bde/g', 'a/bdf/g', 'a/cde/g', 'a/cdf/g' ] ], - [ 'a/{b,c}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g' ] ], - [ 'a/{b,c}{e,f}{g,h,i}/k', {}, [ 'a/beg/k', 'a/beh/k', 'a/bei/k', 'a/bfg/k', 'a/bfh/k', 'a/bfi/k', 'a/ceg/k', 'a/ceh/k', 'a/cei/k', 'a/cfg/k', 'a/cfh/k', 'a/cfi/k' ] ], - [ 'a/{b,{c,d},e}/f', {}, [ 'a/b/f', 'a/c/f', 'a/d/f', 'a/e/f' ] ], - [ 'a/{b,{c,d}/{e,f},g}/h', {}, [ 'a/b/h', 'a/c/e/h', 'a/c/f/h', 'a/d/e/h', 'a/d/f/h', 'a/g/h' ] ], - [ 'a/{b{c,d},e{f,g}h{i,j}}/k', {}, [ 'a/bc/k', 'a/bd/k', 'a/efhi/k', 'a/efhj/k', 'a/eghi/k', 'a/eghj/k' ] ], - [ 'a/{b{c,d},e}/f', {}, [ 'a/bc/f', 'a/bd/f', 'a/e/f' ] ], - [ 'a/{b{c,d}e{f,g}h{i,j}}/k', {}, [ 'a/{bcefhi}/k', 'a/{bcefhj}/k', 'a/{bceghi}/k', 'a/{bceghj}/k', 'a/{bdefhi}/k', 'a/{bdefhj}/k', 'a/{bdeghi}/k', 'a/{bdeghj}/k' ] ], - [ 'a/{b{c,d}e{f,g},h{i,j}}/k', {}, [ 'a/bcef/k', 'a/bceg/k', 'a/bdef/k', 'a/bdeg/k', 'a/hi/k', 'a/hj/k' ] ], - [ 'a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, [ 'a/x/1cdf.md', 'a/x/1cdf.txt', 'a/x/1cef.md', 'a/x/1cef.txt', 'a/x/2cdf.md', 'a/x/2cdf.txt', 'a/x/2cef.md', 'a/x/2cef.txt', 'a/x/3cdf.md', 'a/x/3cdf.txt', 'a/x/3cef.md', 'a/x/3cef.txt', 'a/x/4cdf.md', 'a/x/4cdf.txt', 'a/x/4cef.md', 'a/x/4cef.txt', 'a/x/5cdf.md', 'a/x/5cdf.txt', 'a/x/5cef.md', 'a/x/5cef.txt', 'a/y/1cdf.md', 'a/y/1cdf.txt', 'a/y/1cef.md', 'a/y/1cef.txt', 'a/y/2cdf.md', 'a/y/2cdf.txt', 'a/y/2cef.md', 'a/y/2cef.txt', 'a/y/3cdf.md', 'a/y/3cdf.txt', 'a/y/3cef.md', 'a/y/3cef.txt', 'a/y/4cdf.md', 'a/y/4cdf.txt', 'a/y/4cef.md', 'a/y/4cef.txt', 'a/y/5cdf.md', 'a/y/5cdf.txt', 'a/y/5cef.md', 'a/y/5cef.txt' ] ], - [ 'a/{x,z}{b,{c,d}/{e,f},g}/h', {}, [ 'a/xb/h', 'a/xc/e/h', 'a/xc/f/h', 'a/xd/e/h', 'a/xd/f/h', 'a/xg/h', 'a/zb/h', 'a/zc/e/h', 'a/zc/f/h', 'a/zd/e/h', 'a/zd/f/h', 'a/zg/h' ] ], - [ 'a/{x,{1..5},y}/c{d}e', {}, [ 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/x/c{d}e', 'a/y/c{d}e' ] ], - [ 'a/{{a,b}/{c,d}}/z', {}, [ 'a/{a/c}/z', 'a/{a/d}/z', 'a/{b/c}/z', 'a/{b/d}/z' ] ], - [ 'a/{{b,c}/{d,e}}', {}, [ 'a/{b/d}', 'a/{b/e}', 'a/{c/d}', 'a/{c/e}' ] ], - [ 'a/{{b,c}/{d,e}}/f', {}, [ 'a/{b/d}/f', 'a/{b/e}/f', 'a/{c/d}/f', 'a/{c/e}/f' ] ], - [ 'a{0..3}d', {}, [ 'a0d', 'a1d', 'a2d', 'a3d' ] ], - [ 'a{b}c', {}, [ 'a{b}c' ] ], - [ 'foo {1,2} bar', {}, [ 'foo 1 bar', 'foo 2 bar' ] ], - [ 'x{10..1}y', {}, [ 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y' ] ], - [ 'x{3..3}y', {}, [ 'x3y' ] ], - [ '{ }', {}, [ '{ }' ] ], - [ '{', {}, [ '{' ] ], - [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ], - [ '{10..1}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - [ '{3..3}', {}, [ '3' ] ], - [ '{5..8}', {}, [ '5', '6', '7', '8' ] ], - [ '{9..-4}', {}, [ '-1', '-2', '-3', '-4', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - [ '{a,b,{c,d},e}', {}, [ 'a', 'b', 'c', 'd', 'e' ] ], - [ '{a,b,{c,d}e}', {}, [ 'a', 'b', 'ce', 'de' ] ], - [ '{a,b,{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ], - [ '{a,b{c,d}}', {}, [ 'a', 'bc', 'bd' ] ], - [ '{a,b}/{c,d}', {}, [ 'a/c', 'a/d', 'b/c', 'b/d' ] ], - [ '{a,b}c,d\\}', {}, [ 'ac,d}', 'bc,d}' ] ], - [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ], - [ '{a,b}{c,d}', {}, [ 'ac', 'ad', 'bc', 'bd' ] ], - [ '{abc}', {}, [ '{abc}' ] ], - [ '{b{c,d},e}', {}, [ 'bc', 'bd', 'e' ] ], - [ '{b{c,d},e}/f', {}, [ 'bc/f', 'bd/f', 'e/f' ] ], - [ 'x,y,{abc},trie', {}, [ 'x,y,{abc},trie' ] ], - [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ '{{a,b},{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ], - [ '{{a,b}/{c,d}}', {}, [ '{a/c}', '{a/d}', '{b/c}', '{b/d}' ] ], - [ '{{a,b}/{c,d}}/z', {}, [ '{a/c}/z', '{a/d}/z', '{b/c}/z', '{b/d}/z' ] ], - [ '{}', {}, [ '{}' ] ], - - // should ignore globs - [ '}', {}, [ '}' ] ], - - 'should ignore globs', - - [ '{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, [ 'assemble-generate-*.js', 'assemblefile.js', 'generate.js', 'generator.js', 'update-generate-*.js', 'updatefile.js', 'verb-generate-*.js', 'verbfile.js' ] ], - [ '**/{foo,bar}.js', {}, [ '**/bar.js', '**/foo.js' ] ], - [ '**/{1..5}/a.js', {}, [ '**/1/a.js', '**/2/a.js', '**/3/a.js', '**/4/a.js', '**/5/a.js' ] ], - [ '**/{a,b,c}/*.js', {}, [ '**/a/*.js', '**/b/*.js', '**/c/*.js' ] ], - [ '**/{a,b,*}/*.js', {}, [ '**/*/*.js', '**/a/*.js', '**/b/*.js' ] ], - [ '**/{**,b,*}/*.js', {}, [ '**/**/*.js', '**/*/*.js', '**/b/*.js' ] ], - [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ], - [ 'ff{c,b,a}', {}, [ 'ffa', 'ffb', 'ffc' ] ], - [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ], - [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'xbracesy' ] ], - [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ], - [ '{braces,{0..10}}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ '{l,n,m}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ], - [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ '{{1..10..2},braces}', {}, [ '1', '3', '5', '7', '9', 'braces' ] ], - [ '{{1..10},braces}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ], - [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ], - [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ], - [ '{a,b}{{a,b},a,b}', {}, [ 'aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb' ] ], - [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ], - [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ], - [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ], - [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ], - [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ], - [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ], - [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ], - - 'should gracefully handle large ranges (`braces` handles these fine,', 'they are tested elsewhere, but they break all the other reference libs)', - - [ '{214748364..2147483649}', { skip: true } ], - [ '{2147483645..2147483649}', { skip: true } ], - - 'should handle invalid sets', - - [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ], - [ '{1..10,braces}', {}, [ '1..10', 'braces' ] ], - - 'should not expand escaped braces', - - [ '\\{a,b,c,d,e}', {}, [ '{a,b,c,d,e}' ] ], - [ 'a/\\{b,c}/{d,e}/f', {}, [ 'a/{b,c}/d/f', 'a/{b,c}/e/f' ] ], - [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ], - [ 'a/b/c/{x,y\\}', {}, [ 'a/b/c/{x,y}' ] ], - [ 'a/{z,\\{a,b,c,d,e}/d', {}, [ 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d', 'a/{a/d' ] ], - [ 'abcd{efgh', {}, [ 'abcd{efgh' ] ], - [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ], - [ '{abc}', {}, [ '{abc}' ] ], - [ '{x,y,\\{a,b,c\\}}', {}, [ 'b', 'c}', 'x', 'y', '{a' ] ], - [ '{x,y,{a,b,c\\}}', {}, [ '{x,y,a', '{x,y,b', '{x,y,c}' ] ], - [ '{x,y,{abc},trie}', {}, [ 'trie', 'x', 'y', '{abc}' ] ], - [ './\\{x,y}/{a..z..3}/', {}, [ './{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/' ] ], - - 'should not expand escaped commas', - - [ '{x\\,y,\\{abc\\},trie}', {}, [ 'trie', 'x,y', '{abc}' ] ], - [ 'a{b\\,c\\,d}e', {}, [ 'a{b,c,d}e' ] ], - [ 'a{b\\,c}d', {}, [ 'a{b,c}d' ] ], - [ '{abc\\,def}', {}, [ '{abc,def}' ] ], - [ '{abc\\,def,ghi}', {}, [ 'abc,def', 'ghi' ] ], - [ 'a/{b,c}/{x\\,y}/d/e', {}, [ 'a/b/{x,y}/d/e', 'a/c/{x,y}/d/e' ] ], - - 'should handle empty braces', - - [ '{ }', {}, [ '{ }' ] ], - [ '{', {}, [ '{' ] ], - [ '{}', {}, [ '{}' ] ], - [ '}', {}, [ '}' ] ], - - 'should escape braces when only one value is defined', - - [ 'a{b}c', {}, [ 'a{b}c' ] ], - [ 'a/b/c{d}e', {}, [ 'a/b/c{d}e' ] ], - - 'should escape closing braces when open is not defined', - - [ '{a,b}c,d}', {}, [ 'ac,d}', 'bc,d}' ] ], - - 'should not expand braces in sets with es6/bash-like variables', - - [ 'abc/${ddd}/xyz', {}, [ 'abc/${ddd}/xyz' ] ], - [ 'a${b}c', {}, [ 'a${b}c' ] ], - [ 'a/{${b},c}/d', {}, [ 'a/${b}/d', 'a/c/d' ] ], - [ 'a${b,d}/{foo,bar}c', {}, [ 'a${b,d}/barc', 'a${b,d}/fooc' ] ], - - 'should support sequence brace operators', - - [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ], - [ 'ff{c,b,a}', {}, [ 'ffa', 'ffb', 'ffc' ] ], - [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ], - [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'xbracesy' ] ], - [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ], - [ '{braces,{0..10}}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ '{l,n,m}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ], - [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - [ '{{1..10..2},braces}', {}, [ '1', '3', '5', '7', '9', 'braces' ] ], - [ '{{1..10},braces}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ], - - 'should expand multiple sets', - - [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ], - [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ], - [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ], - - 'should expand nested sets', - - [ '{a,b}{{a,b},a,b}', {}, [ 'aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb' ] ], - [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ], - [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ], - [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ], - [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ], - [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ], - [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ], - [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ], - [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ], - - 'should ignore glob characters', - - [ 'a/b/{d,e}/*.js', {}, [ 'a/b/d/*.js', 'a/b/e/*.js' ] ], - [ 'a/**/c/{d,e}/f*.js', {}, [ 'a/**/c/d/f*.js', 'a/**/c/e/f*.js' ] ], - [ 'a/**/c/{d,e}/f*.{md,txt}', {}, [ 'a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt' ] ], - [ 'a/b/{d,e,[1-5]}/*.js', {}, [ 'a/b/[1-5]/*.js', 'a/b/d/*.js', 'a/b/e/*.js' ] ], - - 'should work with leading and trailing commas', - - [ 'a{b,}c', {}, [ 'abc', 'ac' ] ], - [ 'a{,b}c', {}, [ 'abc', 'ac' ] ], - - 'should handle spaces', - - [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '05 17', '05 18', '05 19', '05 20', '06 10', '06 11', '06 12', '06 13', '06 14', '06 15', '06 16', '06 17', '06 18', '06 19', '06 20', '07 10', '07 11', '07 12', '07 13', '07 14', '07 15', '07 16', '07 17', '07 18', '07 19', '07 20', '08 10', '08 11', '08 12', '08 13', '08 14', '08 15', '08 16', '08 17', '08 18', '08 19', '08 20', '09 10', '09 11', '09 12', '09 13', '09 14', '09 15', '09 16', '09 17', '09 18', '09 19', '09 20' ] ], - [ 'a{ ,c{d, },h}x', {}, [ 'a x', 'ac x', 'acdx', 'ahx' ] ], - [ 'a{ ,c{d, },h} ', {}, [ 'a ', 'ac ', 'acd ', 'ah ' ] ], - - 'see https://github.com/jonschlinkert/microequal/issues/66', - - [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ] ], - - 'should not try to expand ranges with decimals', - - [ '{1.1..2.1}', {}, [ '{1.1..2.1}' ] ], - [ '{1.1..~2.1}', {}, [ '{1.1..~2.1}' ] ], - - 'should escape invalid ranges', - - [ '{1..0f}', {}, [ '{1..0f}' ] ], - [ '{1..10..ff}', {}, [ '{1..10..ff}' ] ], - [ '{1..10.f}', {}, [ '{1..10.f}' ] ], - [ '{1..10f}', {}, [ '{1..10f}' ] ], - [ '{1..20..2f}', {}, [ '{1..20..2f}' ] ], - [ '{1..20..f2}', {}, [ '{1..20..f2}' ] ], - [ '{1..2f..2}', {}, [ '{1..2f..2}' ] ], - [ '{1..ff..2}', {}, [ '{1..ff..2}' ] ], - [ '{1..ff}', {}, [ '{1..ff}' ] ], - [ '{1.20..2}', {}, [ '{1.20..2}' ] ], - - 'should handle weirdly-formed brace expansions (fixed in post-bash-3.1)', - - [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ], - [ 'a-{bdef-{g,i}-c', {}, [ 'a-{bdef-g-c', 'a-{bdef-i-c' ] ], - - 'should not expand quoted strings', - - [ '{"klklkl"}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ], - [ '{"x,x"}', {}, [ '{x,x}' ] ], - [ '{\'x,x\'}', {}, [ '{x,x}' ] ], - - 'should escaped outer braces in nested non-sets', - - [ '{a-{b,c,d}}', {}, [ '{a-b}', '{a-c}', '{a-d}' ] ], - [ '{a,{a-{b,c,d}}}', {}, [ 'a', '{a-b}', '{a-c}', '{a-d}' ] ], - - 'should escape imbalanced braces', - - [ 'abc{', {}, [ 'abc{' ] ], - [ '{abc{', {}, [ '{abc{' ] ], - [ '{abc', {}, [ '{abc' ] ], - [ '}abc', {}, [ '}abc' ] ], - [ 'ab{c', {}, [ 'ab{c' ] ], - [ 'ab{c', {}, [ 'ab{c' ] ], - [ '{{a,b}', {}, [ '{a', '{b' ] ], - [ '{a,b}}', {}, [ 'a}', 'b}' ] ], - [ 'a{b{c{d,e}f}gh', {}, [ 'a{b{cdf}gh', 'a{b{cef}gh' ] ], - [ 'a{b{c{d,e}f}g}h', {}, [ 'a{b{cdf}g}h', 'a{b{cef}g}h' ] ], - [ 'f{x,y{{g,z}}h}', {}, [ 'fx', 'fy{g}h', 'fy{z}h' ] ], - [ 'z{a,b},c}d', {}, [ 'za,c}d', 'zb,c}d' ] ], - [ 'a{b{c{d,e}f{x,y{{g}h', {}, [ 'a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h' ] ], - [ 'f{x,y{{g}h', {}, [ 'f{x,y{{g}h' ] ], - [ 'f{x,y{{g}}h', {}, [ 'f{x,y{{g}}h' ] ], - [ 'a{b{c{d,e}f{x,y{}g}h', {}, [ 'a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh' ] ], - [ 'f{x,y{}g}h', {}, [ 'fxh', 'fy{}gh' ] ], - [ 'z{a,b{,c}d', {}, [ 'z{a,bcd', 'z{a,bd' ] ], - - 'should expand numeric ranges', - - [ 'a{0..3}d', {}, [ 'a0d', 'a1d', 'a2d', 'a3d' ] ], - [ 'x{10..1}y', {}, [ 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y' ] ], - [ 'x{3..3}y', {}, [ 'x3y' ] ], - [ '{1..10}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - [ '{1..3}', {}, [ '1', '2', '3' ] ], - [ '{1..9}', {}, [ '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - [ '{10..1}y', {}, [ '10y', '1y', '2y', '3y', '4y', '5y', '6y', '7y', '8y', '9y' ] ], - [ '{3..3}', {}, [ '3' ] ], - [ '{5..8}', {}, [ '5', '6', '7', '8' ] ], - - 'should expand ranges with negative numbers', - - [ '{-10..-1}', {}, [ '-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9' ] ], - [ '{-20..0}', {}, [ '-1', '-10', '-11', '-12', '-13', '-14', '-15', '-16', '-17', '-18', '-19', '-2', '-20', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0' ] ], - [ '{0..-5}', {}, [ '-1', '-2', '-3', '-4', '-5', '0' ] ], - [ '{9..-4}', {}, [ '-1', '-2', '-3', '-4', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - - 'should expand alphabetical ranges', - - [ '0{1..9}/{10..20}', {}, [ '01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16', '05/17', '05/18', '05/19', '05/20', '06/10', '06/11', '06/12', '06/13', '06/14', '06/15', '06/16', '06/17', '06/18', '06/19', '06/20', '07/10', '07/11', '07/12', '07/13', '07/14', '07/15', '07/16', '07/17', '07/18', '07/19', '07/20', '08/10', '08/11', '08/12', '08/13', '08/14', '08/15', '08/16', '08/17', '08/18', '08/19', '08/20', '09/10', '09/11', '09/12', '09/13', '09/14', '09/15', '09/16', '09/17', '09/18', '09/19', '09/20' ] ], - [ '0{a..d}0', {}, [ '0a0', '0b0', '0c0', '0d0' ] ], - [ 'a/{b..d}/e', {}, [ 'a/b/e', 'a/c/e', 'a/d/e' ] ], - [ '{1..f}', { minimatch: false }, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f' ] ], - [ '{a..A}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' ] ], - [ '{A..a}', {}, [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a' ] ], - [ '{a..e}', {}, [ 'a', 'b', 'c', 'd', 'e' ] ], - [ '{A..E}', {}, [ 'A', 'B', 'C', 'D', 'E' ] ], - [ '{a..f}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ], - [ '{a..z}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ] ], - [ '{E..A}', {}, [ 'A', 'B', 'C', 'D', 'E' ] ], - [ '{f..1}', { minimatch: false }, [ 'f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1' ] ], - [ '{f..a}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ], - [ '{f..f}', {}, [ 'f' ] ], - - 'should expand multiple ranges', - - [ 'a/{b..d}/e/{f..h}', {}, [ 'a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h' ] ], - - 'should expand numerical ranges - positive and negative', - - [ '{-10..10}', {}, [ '-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ], - - 'HEADS UP! If you\'re using the `--mm` flag minimatch freezes on these', 'should expand large numbers', - - [ '{2147483645..2147483649}', { minimatch: false }, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ], - - 'should expand ranges using steps', - - [ '{1..10..1}', { optimize: false }, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ], - [ '{1..10..2}', { optimize: false }, [ '1', '3', '5', '7', '9' ] ], - [ '{1..20..20}', { optimize: false }, [ '1' ] ], - [ '{1..20..20}', { optimize: false }, [ '1' ] ], - [ '{1..20..20}', { optimize: false }, [ '1' ] ], - [ '{1..20..2}', { optimize: false }, [ '1', '3', '5', '7', '9', '11', '13', '15', '17', '19' ] ], - [ '{10..0..2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ], - [ '{10..1..2}', { optimize: false }, [ '10', '8', '6', '4', '2' ] ], - [ '{100..0..5}', { optimize: false }, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ], - [ '{2..10..1}', { optimize: false }, [ '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ], - [ '{2..10..2}', { optimize: false }, [ '2', '4', '6', '8', '10' ] ], - [ '{2..10..3}', { optimize: false }, [ '2', '5', '8' ] ], - [ '{a..z..2}', { optimize: false }, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ], - - 'should expand positive ranges with negative steps', - - [ '{10..0..-2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ], - - 'should expand negative ranges using steps', - - [ '{-1..-10..-2}', { optimize: false }, [ '-1', '-3', '-5', '-7', '-9' ] ], - [ '{-1..-10..2}', { optimize: false }, [ '-1', '-3', '-5', '-7', '-9' ] ], - [ '{-10..-2..2}', { optimize: false }, [ '-10', '-8', '-6', '-4', '-2' ] ], - [ '{-2..-10..1}', { optimize: false }, [ '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10' ] ], - [ '{-2..-10..2}', { optimize: false }, [ '-2', '-4', '-6', '-8', '-10' ] ], - [ '{-2..-10..3}', { optimize: false }, [ '-2', '-5', '-8' ] ], - [ '{-50..-0..5}', { optimize: false }, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ], - [ '{-9..9..3}', { optimize: false }, [ '-9', '-6', '-3', '0', '3', '6', '9' ] ], - [ '{10..1..-2}', { optimize: false }, [ '10', '8', '6', '4', '2' ] ], - [ '{100..0..-5}', { optimize: false }, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ], - - 'should expand alpha ranges with steps', - - [ '{a..e..2}', { optimize: false }, [ 'a', 'c', 'e' ] ], - [ '{E..A..2}', { optimize: false }, [ 'E', 'C', 'A' ] ], - [ '{a..z..2}', { optimize: false }, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ], - [ '{z..a..-2}', { optimize: false }, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ], - - 'should expand alpha ranges with negative steps', - - [ '{z..a..-2}', { optimize: false }, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ], - - 'should handle unwanted zero-padding (fixed post-bash-4.0)', - - [ '{10..0..2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ], - [ '{10..0..-2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ], - [ '{-50..-0..5}', { optimize: false }, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ], - - 'should work with dots in file paths', - - [ '../{1..3}/../foo', {}, [ '../1/../foo', '../2/../foo', '../3/../foo' ] ], - [ '../{2..10..2}/../foo', { optimize: false }, [ '../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo' ] ], - [ '../{1..3}/../{a,b,c}/foo', {}, [ '../1/../a/foo', '../1/../b/foo', '../1/../c/foo', '../2/../a/foo', '../2/../b/foo', '../2/../c/foo', '../3/../a/foo', '../3/../b/foo', '../3/../c/foo' ] ], - [ './{a..z..3}/', { optimize: false }, [ './a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/' ] ], - [ './{"x,y"}/{a..z..3}/', { minimatch: false, optimize: false }, [ './{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/' ] ], - - 'should expand a complex combination of ranges and sets', - - [ 'a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, [ 'a/x/1cdf.md', 'a/x/1cdf.txt', 'a/x/1cef.md', 'a/x/1cef.txt', 'a/x/2cdf.md', 'a/x/2cdf.txt', 'a/x/2cef.md', 'a/x/2cef.txt', 'a/x/3cdf.md', 'a/x/3cdf.txt', 'a/x/3cef.md', 'a/x/3cef.txt', 'a/x/4cdf.md', 'a/x/4cdf.txt', 'a/x/4cef.md', 'a/x/4cef.txt', 'a/x/5cdf.md', 'a/x/5cdf.txt', 'a/x/5cef.md', 'a/x/5cef.txt', 'a/y/1cdf.md', 'a/y/1cdf.txt', 'a/y/1cef.md', 'a/y/1cef.txt', 'a/y/2cdf.md', 'a/y/2cdf.txt', 'a/y/2cef.md', 'a/y/2cef.txt', 'a/y/3cdf.md', 'a/y/3cdf.txt', 'a/y/3cef.md', 'a/y/3cef.txt', 'a/y/4cdf.md', 'a/y/4cdf.txt', 'a/y/4cef.md', 'a/y/4cef.txt', 'a/y/5cdf.md', 'a/y/5cdf.txt', 'a/y/5cef.md', 'a/y/5cef.txt' ] ], - - 'should expand complex sets and ranges in `bash` mode', - - [ 'a/{x,{1..5},y}/c{d}e', {}, [ 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/x/c{d}e', 'a/y/c{d}e' ] ] - ]; - - fixtures.forEach(function(arr) { - if (typeof arr === 'string') { - return; - } - - var options = extend({}, arr[1]); - var pattern = arr[0]; - var expected = arr[2]; - - if (options.skip === true) { - return; - } - - it('should compile: ' + pattern, function() { - equal(pattern, expected, options); - }); - }); -}); diff --git a/test/bash.optimized.js b/test/bash.optimized.js deleted file mode 100644 index f0ce837..0000000 --- a/test/bash.optimized.js +++ /dev/null @@ -1,410 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - var actual = braces.optimize(pattern, options).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} - -/** - * Bash 4.3 unit tests with `braces.optimize()` - */ - -describe('bash.optimized', function() { - var fixtures = [ - ['a{b,c{1..100}/{foo,bar}/,h}x/z', {}, ['a(b|c([1-9]|[1-9][0-9]|100)/(foo|bar)/|h)x/z']], - ['0{1..9} {10..20}', {}, ['0([1-9]) (1[0-9]|20)']], - ['{a,b,c,d,e}', {}, ['(a|b|c|d|e)']], - ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']], - ['a${b}c', {}, ['a${b}c']], - ['a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, ['a/{b,c,d,(x|y)}{e,f}/g']], - ['a/\\{b,c,d\\}\\{e,f\\}/g', {}, ['a/{b,c,d}{e,f}/g']], - ['a/\\{b,c,d\\}\\{e,f}/g', {}, ['a/{b,c,d}{e,f}/g']], - ['a/\\{b,c,d\\}{e,f}/g', {}, ['a/{b,c,d}(e|f)/g']], - ['a/\\{b,c,d{x,y}}{e,f\\}/g', {}, ['a/{b,c,d(x|y)}{e,f}/g']], - ['a/\\{b,c,d}{e,f\\}/g', {}, ['a/{b,c,d}{e,f}/g']], - ['a/\\{b,c,d}{e,f}/g', {}, ['a/{b,c,d}(e|f)/g']], - ['a/\\{x,y}/cde', {}, ['a/{x,y}/cde']], - ['a/\\{{b,c}{e,f}/g', {}, ['a/{(b|c)(e|f)/g']], - ['a/\\{{b,c}{e,f}\\}/g', {}, ['a/{(b|c)(e|f)}/g']], - ['a/\\{{b,c}{e,f}}/g', {}, ['a/{(b|c)(e|f)}/g']], - ['a/b/c/{x,y\\}', {}, ['a/b/c/{x,y}']], - ['a/b/c{d}e', {}, ['a/b/c{d}e']], - ['a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, ['a/b/(b|c|(d|e(f|g)|(w|x)/(y|z)))/h/i']], - ['a/{${b},c}/d', {}, ['a/(${b}|c)/d']], - ['a/{b,c}}{e,f}/g', {}, ['a/(b|c)}(e|f)/g']], - ['a/{b,c\\,d}{e,f}/g', {}, ['a/(b|c,d)(e|f)/g']], - ['a/{b,c\\}}{e,f}/g', {}, ['a/(b|c})(e|f)/g']], - ['a/{b,c}', {}, ['a/(b|c)']], - ['a/{b,c}d{e,f}/g', {}, ['a/(b|c)d(e|f)/g']], - ['a/{b,c}{e,f}/g', {}, ['a/(b|c)(e|f)/g']], - ['a/{b,c}{e,f}{g,h,i}/k', {}, ['a/(b|c)(e|f)(g|h|i)/k']], - ['a/{b,{c,d},e}/f', {}, ['a/(b|(c|d)|e)/f']], - ['a/{b,{c,d}/{e,f},g}/h', {}, ['a/(b|(c|d)/(e|f)|g)/h']], - ['a/{b{c,d},e{f,g}h{i,j}}/k', {}, ['a/(b(c|d)|e(f|g)h(i|j))/k']], - ['a/{b{c,d},e}/f', {}, ['a/(b(c|d)|e)/f']], - ['a/{b{c,d}e{f,g}h{i,j}}/k', {}, ['a/(b(c|d)e(f|g)h(i|j))/k']], - ['a/{b{c,d}e{f,g},h{i,j}}/k', {}, ['a/(b(c|d)e(f|g)|h(i|j))/k']], - ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']], - ['a/{x,z}{b,{c,d}/{e,f},g}/h', {}, ['a/(x|z)(b|(c|d)/(e|f)|g)/h']], - ['a/{x,{1..5},y}/c{d}e', {}, ['a/(x|([1-5])|y)/c{d}e']], - ['a/{{a,b}/{c,d}}/z', {}, ['a/((a|b)/(c|d))/z']], - ['a/{{b,c}/{d,e}}', {}, ['a/((b|c)/(d|e))']], - ['a/{{b,c}/{d,e}}/f', {}, ['a/((b|c)/(d|e))/f']], - ['abc/${ddd}/xyz', {}, ['abc/${ddd}/xyz']], - ['abcd{efgh', {}, ['abcd{efgh']], - ['a{ ,c{d, },h} ', {}, ['a( |c(d| )|h) ']], - ['a{ ,c{d, },h}x', {}, ['a( |c(d| )|h)x']], - ['a{0..3}d', {}, ['a([0-3])d']], - ['a{b{c{d,e}f{x,y{{g}h', {}, ['a{b{c(d|e)f{x,y{{g}h']], - ['a{b}c', {}, ['a{b}c']], - ['foo {1,2} bar', {}, ['foo (1|2) bar']], - ['x{10..1}y', {}, ['x([1-9]|10)y']], - ['x{3..3}y', {}, ['x3y']], - ['{ }', {}, ['{ }']], - ['{', {}, ['{']], - ['{0..10,braces}', {}, ['(0..10|braces)']], - ['{1..0f}', {}, ['{1..0f}']], - ['{1..10,braces}', {}, ['(1..10|braces)']], - ['{1..10..ff}', {}, ['{1..10..ff}']], - ['{1..10.f}', {}, ['{1..10.f}']], - ['{1..10f}', {}, ['{1..10f}']], - ['{1..10}', {}, ['([1-9]|10)']], - ['{1..3}', {}, ['([1-3])']], - ['{1..9}', {}, ['([1-9])']], - ['{1..ff}', {}, ['{1..ff}']], - ['{1.20..2}', {}, ['{1.20..2}']], - ['{10..1}', {}, ['([1-9]|10)']], - ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']], - ['{2147483645..2147483649}', {}, ['(214748364[5-9])']], - ['{3..3}', {}, ['3']], - ['{5..8}', {}, ['([5-8])']], - ['{9..-4}', {}, ['(-[1-4]|[0-9])']], - ['{a,b,{c,d},e}', {}, ['(a|b|(c|d)|e)']], - ['{a,b,{c,d}e}', {}, ['(a|b|(c|d)e)']], - ['{a,b,{c,d}}', {}, ['(a|b|(c|d))']], - ['{a,b{c,d}}', {}, ['(a|b(c|d))']], - ['{a,b}/{c,d}', {}, ['(a|b)/(c|d)']], - ['{a,b}{c,d}', {}, ['(a|b)(c|d)']], - - ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']], - ['{{a,b},{c,d}}', {}, ['((a|b)|(c|d))']], - ['{{a,b}/{c,d}}', {}, ['((a|b)/(c|d))']], - ['{{a,b}/{c,d}}/z', {}, ['((a|b)/(c|d))/z']], - ['{}', {}, ['{}']], - ['}', {}, ['}']], - - // should not process glob characters - ['{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, ['(generate|(assemble|update|verb)(file|-generate-*)|generator).js']], - ['**/{foo,bar}.js', {}, ['**/(foo|bar).js']], - ['**/{1..5}/a.js', {}, ['**/([1-5])/a.js']], - ['**/{a,b,c}/*.js', {}, ['**/(a|b|c)/*.js']], - ['**/{a,b,*}/*.js', {}, ['**/(a|b|*)/*.js']], - ['**/{**,b,*}/*.js', {}, ['**/(**|b|*)/*.js']], - - ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']], - ['ff{c,b,a}', {}, ['ff(c|b|a)']], - ['f{d,e,f}g', {}, ['f(d|e|f)g']], - ['x{{0..10},braces}y', {}, ['x(([0-9]|10)|braces)y']], - ['{1..10}', {}, ['([1-9]|10)']], - ['{a,b,c}', {}, ['(a|b|c)']], - ['{braces,{0..10}}', {}, ['(braces|([0-9]|10))']], - ['{l,n,m}xyz', {}, ['(l|n|m)xyz']], - ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']], - ['{{1..10..2},braces}', {bash: false }, ['((1|3|5|7|9)|braces)']], - ['{{1..10},braces}', {}, ['(([1-9]|10)|braces)']], - - ['a/{a,b}/{c,d}/e', {}, ['a/(a|b)/(c|d)/e']], - ['a{b,c}d{e,f}g', {}, ['a(b|c)d(e|f)g']], - ['a/{x,y}/c{d,e}f.{md,txt}', {}, ['a/(x|y)/c(d|e)f.(md|txt)']], - ['{a,b}{{a,b},a,b}', {}, ['(a|b)((a|b)|a|b)']], - ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']], - ['a{b,c{d,e}f}g', {}, ['a(b|c(d|e)f)g']], - ['a{{x,y},z}b', {}, ['a((x|y)|z)b']], - ['f{x,y{g,z}}h', {}, ['f(x|y(g|z))h']], - ['a{b,c{d,e},h}x/z', {}, ['a(b|c(d|e)|h)x/z']], - ['a{b,c{d,e},h}x{y,z}', {}, ['a(b|c(d|e)|h)x(y|z)']], - ['a{b,c{d,e},{f,g}h}x{y,z}', {}, ['a(b|c(d|e)|(f|g)h)x(y|z)']], - - // should handle invalid sets - ['{0..10,braces}', {}, ['(0..10|braces)']], - ['{1..10,braces}', {}, ['(1..10|braces)']], - - // should not expand escaped braces - ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']], - ['a/b/c/{x,y\\}', {}, ['a/b/c/{x,y}']], - ['a/\\{x,y}/cde', {}, ['a/{x,y}/cde']], - ['abcd{efgh', {}, ['abcd{efgh']], - ['\\{abc\\}', {}, ['{abc}']], - ['{x,y,\\{a,b,c\\}}', {}, ['(x|y|{a|b|c})']], - ['{x,y,{a,b,c\\}}', {}, ['{x,y,(a|b|c})']], - ['{x\\,y,\\{abc\\},trie}', {}, ['(x,y|{abc}|trie)']], - ['{x,y,{abc},trie}', {}, ['(x|y|{abc}|trie)']], - ['x,y,{abc},trie', {}, ['x,y,{abc},trie']], - ['{b{c,d},e}', {}, ['(b(c|d)|e)']], - ['{b{c,d},e}/f', {}, ['(b(c|d)|e)/f']], - ['{abc}', {}, ['{abc}']], - - // should handle empty braces - ['{ }', {}, ['{ }']], - ['{', {}, ['{']], - ['{}', {}, ['{}']], - ['}', {}, ['}']], - - // should escape braces when only one value is defined - ['a{b}c', {}, ['a{b}c']], - ['a/b/c{d}e', {}, ['a/b/c{d}e']], - - // should escape closing braces when open is not defined - ['{a,b}c,d}', {}, ['(a|b)c,d}']], - ['a,b,c,d}', {}, ['a,b,c,d}']], - - // should not expand braces in sets with es6/bash-like variables - ['abc/${ddd}/xyz', {}, ['abc/${ddd}/xyz']], - ['a${b}c', {}, ['a${b}c']], - ['a${b{a,b}}c', {}, ['a${b{a,b}}c']], - ['a/{${b},c}/d', {}, ['a/(${b}|c)/d']], - ['a${b,d}/{foo,bar}c', {}, ['a${b,d}/(foo|bar)c']], - - // should not expand escaped commas - ['a{b\\,c\\,d}e', {}, ['a{b,c,d}e']], - ['a{b\\,c}d', {}, ['a{b,c}d']], - ['{abc\\,def}', {}, ['{abc,def}']], - ['{abc\\,def,ghi}', {}, ['(abc,def|ghi)']], - ['a/{b,c}/{x\\,y}/d/e', {}, ['a/(b|c)/{x,y}/d/e']], - - // should not expand escaped braces - ['{a,b\\}c,d}', {}, ['(a|b}c|d)']], - ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']], - ['a/{z,\\{a,b,c,d,e}/d', {}, ['a/(z|{a|b|c|d|e)/d']], - ['a/\\{b,c}/{d,e}/f', {}, ['a/{b,c}/(d|e)/f']], - ['./\\{x,y}/{a..z..3}/', {}, ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']], - - // should not expand escaped braces or commas - ['{x\\,y,\\{abc\\},trie}', {}, ['(x,y|{abc}|trie)']], - - // should support sequence brace operators - ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']], - ['ff{c,b,a}', {}, ['ff(c|b|a)']], - ['f{d,e,f}g', {}, ['f(d|e|f)g']], - ['x{{0..10},braces}y', {}, ['x(([0-9]|10)|braces)y']], - ['{1..10}', {}, ['([1-9]|10)']], - ['{a,b,c}', {}, ['(a|b|c)']], - ['{braces,{0..10}}', {}, ['(braces|([0-9]|10))']], - ['{l,n,m}xyz', {}, ['(l|n|m)xyz']], - ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']], - ['{{1..10..2},braces}', {}, ['((1|3|5|7|9)|braces)']], - ['{{1..10},braces}', {}, ['(([1-9]|10)|braces)']], - - // should expand multiple sets - ['a/{a,b}/{c,d}/e', {}, ['a/(a|b)/(c|d)/e']], - ['a{b,c}d{e,f}g', {}, ['a(b|c)d(e|f)g']], - ['a/{x,y}/c{d,e}f.{md,txt}', {}, ['a/(x|y)/c(d|e)f.(md|txt)']], - - // should expand nested sets - ['{a,b}{{a,b},a,b}', {}, ['(a|b)((a|b)|a|b)']], - ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']], - ['a{b,c{d,e}f}g', {}, ['a(b|c(d|e)f)g']], - ['a{{x,y},z}b', {}, ['a((x|y)|z)b']], - ['f{x,y{g,z}}h', {}, ['f(x|y(g|z))h']], - ['a{b,c{d,e},h}x/z', {}, ['a(b|c(d|e)|h)x/z']], - ['a{b,c{d,e},h}x{y,z}', {}, ['a(b|c(d|e)|h)x(y|z)']], - ['a{b,c{d,e},{f,g}h}x{y,z}', {}, ['a(b|c(d|e)|(f|g)h)x(y|z)']], - ['a-{b{d,e}}-c', {}, ['a-{b(d|e)}-c']], - - // should expand not modify non-brace characters - ['a/b/{d,e}/*.js', {}, ['a/b/(d|e)/*.js']], - ['a/**/c/{d,e}/f*.js', {}, ['a/**/c/(d|e)/f*.js']], - ['a/**/c/{d,e}/f*.{md,txt}', {}, ['a/**/c/(d|e)/f*.(md|txt)']], - - // should work with leading and trailing commas - ['a{b,}c', {}, ['a(b|)c']], - ['a{,b}c', {}, ['a(|b)c']], - - // should handle spaces - // Bash 4.3 says the this first one should be equivalent to `foo|(1|2)|bar - // That makes sense in Bash, since ' ' is a separator, but not here. - ['foo {1,2} bar', {}, ['foo (1|2) bar']], - ['0{1..9} {10..20}', {}, ['0([1-9]) (1[0-9]|20)']], - ['a{ ,c{d, },h}x', {}, ['a( |c(d| )|h)x']], - ['a{ ,c{d, },h} ', {}, ['a( |c(d| )|h) ']], - - // see https://github.com/jonschlinkert/microequal/issues/66 - ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']], - - /** - * Ranges - */ - - // should not try to expand ranges with decimals - ['{1.1..2.1}', {}, ['{1.1..2.1}']], - ['{1.1..~2.1}', {}, ['{1.1..~2.1}']], - - // should escape invalid ranges - ['{1..0f}', {}, ['{1..0f}']], - ['{1..10..ff}', {}, ['{1..10..ff}']], - ['{1..10.f}', {}, ['{1..10.f}']], - ['{1..10f}', {}, ['{1..10f}']], - ['{1..20..2f}', {}, ['{1..20..2f}']], - ['{1..20..f2}', {}, ['{1..20..f2}']], - ['{1..2f..2}', {}, ['{1..2f..2}']], - ['{1..ff..2}', {}, ['{1..ff..2}']], - ['{1..ff}', {}, ['{1..ff}']], - ['{1.20..2}', {}, ['{1.20..2}']], - - // should handle weirdly-formed brace expansions (fixed in post-bash-3.1) - ['a-{b{d,e}}-c', {}, ['a-{b(d|e)}-c']], - ['a-{bdef-{g,i}-c', {}, ['a-{bdef-(g|i)-c']], - - // should not expand quoted strings - ['{"klklkl"}{1,2,3}', {}, ['{klklkl}(1|2|3)']], - ['{"x,x"}', {}, ['{x,x}']], - - // should escaped outer braces in nested non-sets - ['{a-{b,c,d}}', {}, ['{a-(b|c|d)}']], - ['{a,{a-{b,c,d}}}', {}, ['(a|{a-(b|c|d)})']], - - // should escape imbalanced braces - ['a-{bdef-{g,i}-c', {}, ['a-{bdef-(g|i)-c']], - ['abc{', {}, ['abc{']], - ['{abc{', {}, ['{abc{']], - ['{abc', {}, ['{abc']], - ['}abc', {}, ['}abc']], - ['ab{c', {}, ['ab{c']], - ['ab{c', {}, ['ab{c']], - ['{{a,b}', {}, ['{(a|b)']], - ['{a,b}}', {}, ['(a|b)}']], - ['abcd{efgh', {}, ['abcd{efgh']], - ['a{b{c{d,e}f}gh', {}, ['a{b(c(d|e)f)gh']], - ['a{b{c{d,e}f}g}h', {}, ['a(b(c(d|e)f)g)h']], - ['f{x,y{{g,z}}h}', {}, ['f(x|y((g|z))h)']], - ['z{a,b},c}d', {}, ['z(a|b),c}d']], - ['a{b{c{d,e}f{x,y{{g}h', {}, ['a{b{c(d|e)f{x,y{{g}h']], - ['f{x,y{{g}h', {}, ['f{x,y{{g}h']], - ['f{x,y{{g}}h', {}, ['f{x,y{{g}}h']], - ['a{b{c{d,e}f{x,y{}g}h', {}, ['a{b{c(d|e)f(x|y{}g)h']], - ['f{x,y{}g}h', {}, ['f(x|y{}g)h']], - ['z{a,b{,c}d', {}, ['z{a,b(|c)d']], - - // should expand numeric ranges - ['a{0..3}d', {}, ['a([0-3])d']], - ['x{10..1}y', {}, ['x([1-9]|10)y']], - ['x{3..3}y', {}, ['x3y']], - ['{1..10}', {}, ['([1-9]|10)']], - ['{1..3}', {}, ['([1-3])']], - ['{1..9}', {}, ['([1-9])']], - ['{10..1}', {}, ['([1-9]|10)']], - ['{10..1}y', {}, ['([1-9]|10)y']], - ['{3..3}', {}, ['3']], - ['{5..8}', {}, ['([5-8])']], - - // should expand ranges with negative numbers - ['{-10..-1}', {}, ['(-[1-9]|-10)']], - ['{-20..0}', {}, ['(-[1-9]|-1[0-9]|-20|0)']], - ['{0..-5}', {}, ['(-[1-5]|0)']], - ['{9..-4}', {}, ['(-[1-4]|[0-9])']], - - // should expand alphabetical ranges - ['0{1..9}/{10..20}', {}, ['0([1-9])/(1[0-9]|20)']], - ['0{a..d}0', {}, ['0([a-d])0']], - ['a/{b..d}/e', {}, ['a/([b-d])/e']], - ['{1..f}', {}, ['([1-f])']], - ['{a..A}', {}, ['([A-a])']], - ['{A..a}', {}, ['([A-a])']], - ['{a..e}', {}, ['([a-e])']], - ['{A..E}', {}, ['([A-E])']], - ['{a..f}', {}, ['([a-f])']], - ['{a..z}', {}, ['([a-z])']], - ['{E..A}', {}, ['([A-E])']], - ['{f..1}', {}, ['([1-f])']], - ['{f..a}', {}, ['([a-f])']], - ['{f..f}', {}, ['f']], - - // should expand multiple ranges - ['a/{b..d}/e/{f..h}', {}, ['a/([b-d])/e/([f-h])']], - - // should expand numerical ranges - positive and negative - ['{-10..10}', {}, ['(-[1-9]|-?10|[0-9])']], - - // HEADS UP! If you're using the `--mm` flag minimatch freezes on these - // should expand large numbers - ['{2147483645..2147483649}', {}, ['(214748364[5-9])']], - ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']], - - // should expand ranges using steps - ['{1..10..1}', {bash: false}, ['([1-9]|10)']], - ['{1..10..2}', {bash: false}, ['(1|3|5|7|9)']], - ['{1..20..20}', {bash: false}, ['1']], - ['{1..20..2}', {bash: false}, ['(1|3|5|7|9|11|13|15|17|19)']], - ['{10..0..2}', {bash: false}, ['(10|8|6|4|2|0)']], - ['{10..1..2}', {bash: false}, ['(10|8|6|4|2)']], - ['{100..0..5}', {bash: false}, ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']], - ['{2..10..1}', {bash: false}, ['([2-9]|10)']], - ['{2..10..2}', {bash: false}, ['(2|4|6|8|10)']], - ['{2..10..3}', {bash: false}, ['(2|5|8)']], - ['{a..z..2}', {bash: false}, ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']], - - // should expand positive ranges with negative steps - ['{10..0..-2}', {bash: false}, ['(10|8|6|4|2|0)']], - - // should expand negative ranges using steps - ['{-1..-10..-2}', {bash: false}, ['(-(1|3|5|7|9))']], - ['{-1..-10..2}', {bash: false}, ['(-(1|3|5|7|9))']], - ['{-10..-2..2}', {bash: false}, ['(-(10|8|6|4|2))']], - ['{-2..-10..1}', {bash: false}, ['(-[2-9]|-10)']], - ['{-2..-10..2}', {bash: false}, ['(-(2|4|6|8|10))']], - ['{-2..-10..3}', {bash: false}, ['(-(2|5|8))']], - ['{-50..-0..5}', {bash: false}, ['(0|-(50|45|40|35|30|25|20|15|10|5))']], - ['{-9..9..3}', {bash: false}, ['(0|3|6|9|-(9|6|3))']], - ['{10..1..-2}', {bash: false}, ['(10|8|6|4|2)']], - ['{100..0..-5}', {bash: false}, ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']], - - // should expand alpha ranges with steps - ['{a..e..2}', {bash: false}, ['(a|c|e)']], - ['{E..A..2}', {bash: false}, ['(E|C|A)']], - ['{a..z..2}', {bash: false}, ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']], - ['{z..a..-2}', {bash: false}, ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']], - - // should expand alpha ranges with negative steps - ['{z..a..-2}', {bash: false}, ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']], - - // unwanted zero-padding (fixed post-bash-4.0) - ['{10..0..2}', {bash: false}, ['(10|8|6|4|2|0)']], - ['{10..0..-2}', {bash: false}, ['(10|8|6|4|2|0)']], - ['{-50..-0..5}', {bash: false}, ['(0|-(50|45|40|35|30|25|20|15|10|5))']], - - // should work with dots in file paths - ['../{1..3}/../foo', {}, ['../([1-3])/../foo']], - ['../{2..10..2}/../foo', {}, ['../(2|4|6|8|10)/../foo']], - ['../{1..3}/../{a,b,c}/foo', {}, ['../([1-3])/../(a|b|c)/foo']], - ['./{a..z..3}/', {}, ['./(a|d|g|j|m|p|s|v|y)/']], - ['./{"x,y"}/{a..z..3}/', {}, ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']], - - // should expand a complex combination of ranges and sets - ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']], - - // should expand complex sets and ranges in `bash` mode - ['a/{x,{1..5},y}/c{d}e', {}, ['a/(x|([1-5])|y)/c{d}e']] - ]; - - fixtures.forEach(function(arr) { - if (typeof arr === 'string') { - return; - } - - var options = extend({}, arr[1]); - var pattern = arr[0]; - var expected = arr[2]; - - if (options.skip === true) { - return; - } - - it('should compile: ' + pattern, function() { - equal(pattern, expected, options); - }); - }); -}); diff --git a/test/brace-expansion.js b/test/brace-expansion.js deleted file mode 100644 index af729d8..0000000 --- a/test/brace-expansion.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options).sort(); - assert.deepEqual(actual.filter(Boolean), expected.sort(), pattern); -} - -/** - * All of the unit tests from brace-expansion v1.1.6 - * https://github.com/juliangruber/brace-expansion - */ - -describe('unit tests from brace-expand', function() { - describe('sequences', function() { - it('numeric sequences', function() { - equal('a{1..2}b{2..3}c', ['a1b2c', 'a1b3c', 'a2b2c', 'a2b3c']); - equal('{1..2}{2..3}', ['12', '13', '22', '23']); - }); - - it('numeric sequences with step count', function() { - equal('{0..8..2}', ['0', '2', '4', '6', '8']); - equal('{1..8..2}', ['1', '3', '5', '7']); - }); - - it('numeric sequence with negative x / y', function() { - equal('{3..-2}', ['3', '2', '1', '0', '-1', '-2']); - }); - - it('alphabetic sequences', function() { - equal('1{a..b}2{b..c}3', ['1a2b3', '1a2c3', '1b2b3', '1b2c3']); - equal('{a..b}{b..c}', ['ab', 'ac', 'bb', 'bc']); - }); - - it('alphabetic sequences with step count', function() { - equal('{a..k..2}', ['a', 'c', 'e', 'g', 'i', 'k']); - equal('{b..k..2}', ['b', 'd', 'f', 'h', 'j']); - }); - }); - - describe('dollar', function() { - it('ignores ${', function() { - equal('${1..3}', ['${1..3}']); - equal('${a,b}${c,d}', ['${a,b}${c,d}']); - equal('x${a,b}x${c,d}x', ['x${a,b}x${c,d}x']); - }); - }); - - describe('empty option', function() { - it('should support empty sets', function() { - equal('-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']); - }); - }); - - describe('negative increments', function() { - it('should support negative steps', function() { - equal('{3..1}', ['3', '2', '1']); - equal('{10..8}', ['10', '9', '8']); - equal('{10..08}', ['10', '09', '08']); - equal('{c..a}', ['c', 'b', 'a']); - - equal('{4..0..2}', ['4', '2', '0']); - equal('{4..0..-2}', ['4', '2', '0']); - equal('{e..a..2}', ['e', 'c', 'a']); - }); - }); - - describe('nested', function() { - it('should support nested sets', function() { - equal('{a,b{1..3},c}', ['a', 'b1', 'b2', 'b3', 'c']); - equal('{{A..Z},{a..z}}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].sort()); - equal('ppp{,config,oe{,conf}}', ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']); - }); - }); - - describe('order', function() { - it('should expand in given order', function() { - equal('a{d,c,b}e', ['ade', 'ace', 'abe']); - }); - }); - - describe('pad', function() { - it('should support padding', function() { - equal('{9..11}', ['9', '10', '11']); - equal('{09..11}', ['09', '10', '11']); - }); - }); -}); - diff --git a/test/braces.compile.js b/test/braces.compile.js index 03a5be6..f1b8395 100644 --- a/test/braces.compile.js +++ b/test/braces.compile.js @@ -1,19 +1,59 @@ 'use strict'; require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -describe('.compile', function() { - it('should return an object', function() { - var res = braces.compile('a/{b,c}/d'); - assert(res); - assert.equal(typeof res, 'object'); +const assert = require('assert').strict; +const compile = require('../lib/compile'); +const parse = require('../lib/parse'); + +describe('braces.compile()', () => { + describe('errors', () => { + it('should throw an error when invalid args are passed', () => { + assert.throws(() => compile()); + }); + }); + + describe('invalid characters', () => { + it('should escape invalid bracket characters', () => { + assert.equal(compile(parse(']{a,b,c}')), '\\](a|b|c)'); + }); + }); + + describe('sets', () => { + it('should support empty sets', () => { + assert.equal(compile(parse('{a,}')), '(a|)'); + assert.equal(compile(parse('{a,,}')), '(a|)'); + assert.equal(compile(parse('{a,,,}')), '(a|)'); + assert.equal(compile(parse('{a,,,,}')), '(a|)'); + assert.equal(compile(parse('{a,,,,,}')), '(a|)'); + }); }); - it('should return output as an array', function() { - var res = braces.compile('a/{b,c}/d'); - assert(Array.isArray(res.output)); - assert.deepEqual(res.output, ['a/(b|c)/d']); + describe('ranges', () => { + it('should escape braces with invalid ranges', () => { + assert.equal(compile(parse('{a...b}')), '{a...b}'); + assert.equal(compile(parse('{a...b}'), { escapeInvalid: true }), '\\{a...b\\}'); + }); + + it('should expand brace patterns with both sets and ranges', () => { + assert.equal(compile(parse('{a..e,z}')), '(a..e|z)'); + assert.equal(compile(parse('{a..e,a..z}')), '(a..e|a..z)'); + }); + + it('should escape braces with too many range expressions', () => { + assert.equal(compile(parse('{a..e..x..z}')), '{a..e..x..z}'); + assert.equal(compile(parse('{a..e..x..z}'), { escapeInvalid: true }), '\\{a..e..x..z\\}'); + }); + }); + + describe('invalid', () => { + it('should escape incomplete brace patterns', () => { + assert.equal(compile(parse(']{a/b')), '\\]{a/b'); + assert.equal(compile(parse(']{a/b'), { escapeInvalid: true }), '\\]\\{a/b'); + }); + + it('should escape non-brace patterns (no sets or ranges)', () => { + assert.equal(compile(parse(']{a/b}')), '\\]{a/b}'); + assert.equal(compile(parse(']{a/b}'), { escapeInvalid: true }), '\\]\\{a/b\\}'); + }); }); }); diff --git a/test/braces.create.js b/test/braces.create.js deleted file mode 100644 index ee733e0..0000000 --- a/test/braces.create.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -describe('.makeRe', function() { - it('should throw an error when invalid args are passed', function() { - assert.throws(function() { - braces.makeRe(); - }); - }); - - it('should throw an error when string exceeds max safe length', function() { - var MAX_LENGTH = 1024 * 64; - - assert.throws(function() { - braces.makeRe(Array(MAX_LENGTH + 1).join('.')); - }); - }); -}); diff --git a/test/braces.expand.js b/test/braces.expand.js new file mode 100644 index 0000000..23cd4df --- /dev/null +++ b/test/braces.expand.js @@ -0,0 +1,168 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const expand = require('../lib/expand'); +const parse = require('../lib/parse'); +const bashPath = require('bash-path'); +const cp = require('child_process'); +const braces = require('..'); + +const bash = input => { + return cp.spawnSync(bashPath(), ['-c', `echo ${input}`]) + .stdout.toString() + .split(/\s+/) + .filter(Boolean); +}; + +const equal = (input, expected = bash(input), options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; + +describe('unit tests from brace-expand', () => { + describe('extglobs', () => { + it('should split on commas when braces are inside extglobs', () => { + equal('*(a|{b|c,d})', ['*(a|b|c)', '*(a|d)']); + }); + + it('should not split on commas in extglobs when inside braces', () => { + equal('{a,@(b,c)}', ['a', '@(b,c)']); + equal('{a,*(b|c,d)}', ['a', '*(b|c,d)']); + }); + }); + + describe('expand', () => { + it('should expand an AST', () => { + assert.deepEqual(expand(parse('a/{b,c}/d')), ['a/b/d', 'a/c/d']); + }); + + it('should support expanded nested empty sets', () => { + equal('{\`foo,bar\`}', ['{`foo,bar`}'], { keepQuotes: true }); + equal('{\\`foo,bar\\`}', ['`foo', 'bar`'], { keepQuotes: true }); + equal('{`foo\,bar`}', ['{`foo,bar`}'], { keepQuotes: true }); + equal('{`foo\\,bar`}', ['{`foo\\,bar`}'], { keepQuotes: true }); + + equal('{\`foo,bar\`}', ['{foo,bar}']); + equal('{\\`foo,bar\\`}', ['`foo', 'bar`']); + equal('{`foo\,bar`}', ['{foo,bar}']); + equal('{`foo\\,bar`}', ['{foo\\,bar}']); + + equal('{a,\\\\{a,b}c}', ['a', '\\ac', '\\bc']); + equal('{a,\\{a,b}c}', ['ac}', '{ac}', 'bc}']); + equal('{,eno,thro,ro}ugh', ['ugh', 'enough', 'through', 'rough']); + equal('{,{,eno,thro,ro}ugh}{,out}', ['', 'out', 'ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout']); + equal('{{,eno,thro,ro}ugh,}{,out}', ['ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout', '', 'out']); + equal('{,{,a,b}z}{,c}', ['', 'c', 'z', 'zc', 'az', 'azc', 'bz', 'bzc']); + equal('{,{,a,b}z}{c,}', ['c', '', 'zc', 'z', 'azc', 'az', 'bzc', 'bz']); + equal('{,{,a,b}z}{,c,}', ['', 'c', '', 'z', 'zc', 'z', 'az', 'azc', 'az', 'bz', 'bzc', 'bz']); + equal('{,{,a,b}z}{c,d}', ['c', 'd', 'zc', 'zd', 'azc', 'azd', 'bzc', 'bzd']); + equal('{{,a,b}z,}{,c}', ['z', 'zc', 'az', 'azc', 'bz', 'bzc', '', 'c']); + equal('{,a{,b}z,}{,c}', ['', 'c', 'az', 'azc', 'abz', 'abzc', '', 'c']); + equal('{,a{,b},}{,c}', ['', 'c', 'a', 'ac', 'ab', 'abc', '', 'c']); + equal('{,a{,b}}{,c}', ['', 'c', 'a', 'ac', 'ab', 'abc']); + equal('{,b}{,d}', ['', 'd', 'b', 'bd']); + equal('{a,b}{,d}', ['a', 'ad', 'b', 'bd']); + equal('{,a}{z,c}', ['z', 'c', 'az', 'ac']); + equal('{,{a,}}{z,c}', ['z', 'c', 'az', 'ac', 'z', 'c']); + equal('{,{,a}}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']); + equal('{,{,a},}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac', 'z', 'c']); + equal('{{,,a}}{z,c}', [ '{}z', '{}c', '{}z', '{}c', '{a}z', '{a}c' ]); + equal('{{,a},}{z,c}', ['z', 'c', 'az', 'ac', 'z', 'c']); + equal('{,,a}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']); + equal('{,{,}}{z,c}', ['z', 'c', 'z', 'c', 'z', 'c']); + equal('{,{a,b}}{,c}', ['', 'c', 'a', 'ac', 'b', 'bc']); + equal('{,{a,}}{,c}', ['', 'c', 'a', 'ac', '', 'c']); + equal('{,{,b}}{,c}', ['', 'c', '', 'c', 'b', 'bc']); + equal('{,{,}}{,c}', ['', 'c', '', 'c', '', 'c']); + equal('{,a}{,c}', ['', 'c', 'a', 'ac']); + equal('{,{,a}b}', ['', 'b', 'ab']); + equal('{,b}', ['', 'b']); + equal('{,b{,a}}', ['', 'b', 'ba']); + equal('{b,{,a}}', ['b', '', 'a']); + equal('{,b}{,d}', ['', 'd', 'b', 'bd']); + equal('{a,b}{,d}', ['a', 'ad', 'b', 'bd']); + }); + }); + + /** + * The following unit tests are from brace-expansion v1.1.6 + * https://github.com/juliangruber/brace-expansion + */ + + describe('brace expansion unit tests from brace-expand', () => { + describe('sequences', () => { + it('numeric sequences', () => { + equal('a{1..2}b{2..3}c', ['a1b2c', 'a1b3c', 'a2b2c', 'a2b3c']); + equal('{1..2}{2..3}', ['12', '13', '22', '23']); + }); + + it('numeric sequences with step count', () => { + equal('{0..8..2}', ['0', '2', '4', '6', '8']); + equal('{1..8..2}', ['1', '3', '5', '7']); + }); + + it('numeric sequence with negative x / y', () => { + equal('{3..-2}', ['3', '2', '1', '0', '-1', '-2']); + }); + + it('alphabetic sequences', () => { + equal('1{a..b}2{b..c}3', ['1a2b3', '1a2c3', '1b2b3', '1b2c3']); + equal('{a..b}{b..c}', ['ab', 'ac', 'bb', 'bc']); + }); + + it('alphabetic sequences with step count', () => { + equal('{a..k..2}', ['a', 'c', 'e', 'g', 'i', 'k']); + equal('{b..k..2}', ['b', 'd', 'f', 'h', 'j']); + }); + }); + + describe('dollar', () => { + it('ignores ${', () => { + equal('${1..3}', ['${1..3}']); + equal('${a,b}${c,d}', ['${a,b}${c,d}']); + equal('x${a,b}x${c,d}x', ['x${a,b}x${c,d}x']); + }); + }); + + describe('empty option', () => { + it('should support empty sets', () => { + equal('-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']); + }); + }); + + describe('negative increments', () => { + it('should support negative steps', () => { + equal('{3..1}', ['3', '2', '1']); + equal('{10..8}', ['10', '9', '8']); + equal('{10..08}', ['10', '09', '08']); + equal('{c..a}', ['c', 'b', 'a']); + + equal('{4..0..2}', ['4', '2', '0']); + equal('{4..0..-2}', ['4', '2', '0']); + equal('{e..a..2}', ['e', 'c', 'a']); + }); + }); + + describe('nested', () => { + it('should support nested sets', () => { + equal('{a,b{1..3},c}', ['a', 'b1', 'b2', 'b3', 'c']); + equal('{{A..E},{a..e}}', ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']); + equal('ppp{,config,oe{,conf}}', ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']); + }); + }); + + describe('order', () => { + it('should expand in given order', () => { + equal('a{d,c,b}e', ['ade', 'ace', 'abe']); + }); + }); + + describe('pad', () => { + it('should support padding', () => { + equal('{9..11}', ['9', '10', '11']); + equal('{09..11}', ['09', '10', '11']); + }); + }); + }); +}); + diff --git a/test/braces.js b/test/braces.js deleted file mode 100644 index 19d9868..0000000 --- a/test/braces.js +++ /dev/null @@ -1,146 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - assert.deepEqual(braces(pattern, options), expected); -} - -equal.expand = function(pattern, expected, options) { - var actual = braces.expand(pattern, options).sort(); - assert.deepEqual(actual.filter(Boolean), expected.sort(), pattern); -}; - -describe('braces', function() { - it('should return an array', function() { - assert(Array.isArray(braces('{a,b}'))); - }); - - it('should return an optimized string by default', function() { - equal('a/{b,c}/d', ['a/(b|c)/d']); - }); - - it('should return an expanded array if defined on options', function() { - equal('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true}); - }); - - it('should optimize an array of patterns', function() { - equal(['a/{b,c}/d', 'x/{foo,bar}/z'], ['a/(b|c)/d', 'x/(foo|bar)/z']); - }); - - it('should expand an array of patterns', function() { - var actual = braces(['a/{b,c}/d', 'a/{b,c}/d']); - assert.deepEqual(actual, ['a/(b|c)/d', 'a/(b|c)/d']); - }); - - it('should not uniquify by default', function() { - var actual = braces(['a/{b,c}/d', 'a/{b,c}/d']); - assert.deepEqual(actual, ['a/(b|c)/d', 'a/(b|c)/d']); - }); - - it('should uniquify when `options.nodupes` is true', function() { - var actual = braces(['a/{b,c}/d', 'a/{b,c}/d'], {nodupes: true}); - assert.deepEqual(actual, ['a/(b|c)/d']); - }); - - it('should expand ranges', function() { - equal('a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'], {expand: true}); - }); - - it('should expand ranges that are nested in a set', function() { - equal('a{b,c,{1..5}}e', ['abe', 'ace', 'a1e', 'a2e', 'a3e', 'a4e', 'a5e'], {expand: true}); - }); - - it('should not expand ranges when they are just characters in a set', function() { - equal('a{b,c,1..5}e', ['abe', 'ace', 'a1..5e'], {expand: true}); - equal('a{/../}e', ['a/e'], {expand: true}); - equal('a{/../,z}e', ['a/../e', 'aze'], {expand: true}); - equal('a{b,c/*/../d}e', ['abe', 'ac/*/../de'], {expand: true}); - equal('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd'], {expand: true}); - }); - - it('should support expanded nested empty sets', function() { - equal.expand('{\`foo,bar\`}', ['{foo,bar}']); - equal.expand('{\\`foo,bar\\`}', ['`foo', 'bar`']); - equal.expand('{`foo\,bar`}', ['{foo,bar}']); - equal.expand('{`foo\\,bar`}', ['{`foo\\,bar`}']); - equal.expand('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']); - equal.expand('{a,\\{a,b}c}', ['ac}', '{ac}', 'bc}']); - equal.expand('{,eno,thro,ro}ugh', ['ugh', 'enough', 'through', 'rough']); - equal.expand('{,{,eno,thro,ro}ugh}{,out}', ['out', 'ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout']); - equal.expand('{{,eno,thro,ro}ugh,}{,out}', ['ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout', 'out']); - equal.expand('{,{,a,b}z}{,c}', ['c', 'z', 'zc', 'az', 'azc', 'bz', 'bzc']); - equal.expand('{,{,a,b}z}{c,}', ['c', 'zc', 'z', 'azc', 'az', 'bzc', 'bz']); - equal.expand('{,{,a,b}z}{,c,}', ['c', 'z', 'zc', 'z', 'az', 'azc', 'az', 'bz', 'bzc', 'bz']); - equal.expand('{,{,a,b}z}{c,d}', ['c', 'd', 'zc', 'zd', 'azc', 'azd', 'bzc', 'bzd']); - equal.expand('{{,a,b}z,}{,c}', ['z', 'zc', 'az', 'azc', 'bz', 'bzc', 'c']); - equal.expand('{,a{,b}z,}{,c}', ['c', 'az', 'azc', 'abz', 'abzc', 'c']); - equal.expand('{,a{,b},}{,c}', ['c', 'a', 'ac', 'ab', 'abc', 'c']); - equal.expand('{,a{,b}}{,c}', ['c', 'a', 'ac', 'ab', 'abc']); - equal.expand('{,b}{,d}', ['d', 'b', 'bd']); - equal.expand('{a,b}{,d}', ['a', 'ad', 'b', 'bd']); - equal.expand('{,a}{z,c}', ['z', 'c', 'az', 'ac']); - equal.expand('{,{a,}}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']); - equal.expand('{,{,a}}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']); - equal.expand('{,{,a},}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac', 'z', 'c']); - equal.expand('{{,,a}}{z,c}', [ '{}z', '{}c', '{}z', '{}c', '{a}z', '{a}c' ]); - equal.expand('{{,a},}{z,c}', ['z', 'c', 'az', 'ac', 'z', 'c']); - equal.expand('{,,a}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']); - equal.expand('{,{,}}{z,c}', ['z', 'c', 'z', 'c', 'z', 'c']); - equal.expand('{,{a,b}}{,c}', ['c', 'a', 'ac', 'b', 'bc']); - equal.expand('{,{a,}}{,c}', ['c', 'a', 'ac', 'c']); - equal.expand('{,{,b}}{,c}', ['c', 'c', 'b', 'bc']); - equal.expand('{,{,}}{,c}', ['c', 'c', 'c']); - equal.expand('{,a}{,c}', ['c', 'a', 'ac']); - equal.expand('{,{,a}b}', ['b', 'ab']); - equal.expand('{,b}', ['b']); - equal.expand('{,b{,a}}', ['b', 'ba']); - equal.expand('{b,{,a}}', ['b', 'a']); - equal.expand('{,b}{,d}', ['d', 'b', 'bd']); - equal.expand('{a,b}{,d}', ['a', 'ad', 'b', 'bd']); - }); - - it('should support optimized nested empty sets', function() { - equal('{\`foo,bar\`}', [ '{foo,bar}' ]); - equal('{\\`foo,bar\\`}', [ '(`foo|bar`)' ]); - equal('{`foo\,bar`}', [ '{foo,bar}' ]); - equal('{`foo\\,bar`}', [ '(`foo\\,bar`)' ]); - equal('{a,\\\\{a,b}c}', [ '(a|\\\\(a|b)c)' ]); - equal('{a,\\{a,b}c}', [ '(a|{a|b)c}' ]); - equal('a/{\\{b,c,d},z}/e', [ 'a/({b|c|d),z}/e' ]); - equal('{,eno,thro,ro}ugh', [ '(|eno|thro|ro)ugh' ]); - equal('{,{,eno,thro,ro}ugh}{,out}', [ '((|eno|thro|ro)ugh|)(|out)' ]); - equal('{{,eno,thro,ro}ugh,}{,out}', [ '((|eno|thro|ro)ugh|)(|out)' ]); - equal('{,{,a,b}z}{,c}', [ '((|a|b)z|)(|c)' ]); - equal('{,{,a,b}z}{c,}', [ '((|a|b)z|)(c|)' ]); - equal('{,{,a,b}z}{,c,}', [ '((|a|b)z|)(|c|)' ]); - equal('{,{,a,b}z}{c,d}', [ '((|a|b)z|)(c|d)' ]); - equal('{{,a,b}z,}{,c}', [ '((|a|b)z|)(|c)' ]); - equal('{,a{,b}z,}{,c}', [ '(|a(|b)z|)(|c)' ]); - equal('{,a{,b},}{,c}', [ '(|a(|b)|)(|c)' ]); - equal('{,a{,b}}{,c}', [ '(|a(|b))(|c)' ]); - equal('{,b}{,d}', [ '(|b)(|d)' ]); - equal('{a,b}{,d}', [ '(a|b)(|d)' ]); - equal('{,a}{z,c}', [ '(|a)(z|c)' ]); - equal('{,{a,}}{z,c}', [ '((a|)|)(z|c)' ]); - equal('{,{,a}}{z,c}', [ '((|a)|)(z|c)' ]); - equal('{,{,a},}{z,c}', [ '((|a)||)(z|c)' ]); - equal('{{,,a}}{z,c}', [ '((||a))(z|c)' ]); - equal('{{,a},}{z,c}', [ '((|a)|)(z|c)' ]); - equal('{,,a}{z,c}', [ '(||a)(z|c)' ]); - equal('{,{,}}{z,c}', [ '(z|c)' ]); - equal('{,{a,b}}{,c}', [ '((a|b)|)(|c)' ]); - equal('{,{a,}}{,c}', [ '((a|)|)(|c)' ]); - equal('{,{,b}}{,c}', [ '((|b)|)(|c)' ]); - equal('{,{,}}{,c}', [ '(|c)' ]); - equal('{,a}{,c}', [ '(|a)(|c)' ]); - equal('{,{,a}b}', [ '((|a)b|)' ]); - equal('{,b}', [ '(|b)' ]); - equal('{,b{,a}}', [ '(|b(|a))' ]); - equal('{b,{,a}}', [ '(b|(|a))' ]); - equal('{,b}{,d}', [ '(|b)(|d)' ]); - equal('{a,b}{,d}', [ '(a|b)(|d)' ]); - }); -}); diff --git a/test/braces.makeRe.js b/test/braces.makeRe.js deleted file mode 100644 index ae02896..0000000 --- a/test/braces.makeRe.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -describe('.create', function() { - it('should throw an error when invalid args are passed', function() { - assert.throws(function() { - braces.create(); - }); - }); - - it('should throw an error when string exceeds max safe length', function() { - var MAX_LENGTH = 1024 * 64; - - assert.throws(function() { - braces.create(Array(MAX_LENGTH + 1).join('.')); - }); - }); -}); diff --git a/test/braces.parse.js b/test/braces.parse.js index 7e94f44..b814558 100644 --- a/test/braces.parse.js +++ b/test/braces.parse.js @@ -1,18 +1,49 @@ 'use strict'; require('mocha'); -var assert = require('assert'); -var braces = require('..'); +const assert = require('assert').strict; +const parse = require('../lib/parse'); -describe('.parse', function() { - it('should return an AST object', function() { - var ast = braces.parse('a/{b,c}/d'); - assert(ast); - assert.equal(typeof ast, 'object'); +describe('braces.parse()', () => { + describe('errors', () => { + it('should throw an error when string exceeds max safe length', () => { + let MAX_LENGTH = 1024 * 64; + assert.throws(() => parse('.'.repeat(MAX_LENGTH + 2))); + }); }); - it('should have an array of nodes', function() { - var ast = braces.parse('a/{b,c}/d'); - assert(Array.isArray(ast.nodes)); + describe('valid', () => { + it('should return an AST', () => { + let ast = parse('a/{b,c}/d'); + let brace = ast.nodes.find(node => node.type === 'brace'); + assert(brace); + assert.equal(brace.nodes.length, 5); + }); + + it('should ignore braces inside brackets', () => { + let ast = parse('a/[{b,c}]/d'); + assert.equal(ast.nodes[1].type, 'text'); + assert.equal(ast.nodes[1].value, 'a/[{b,c}]/d'); + }); + + it('should parse braces with brackets inside', () => { + let ast = parse('a/{a,b,[{c,d}]}/e'); + let brace = ast.nodes[2]; + let bracket = brace.nodes.find(node => node.value[0] === '['); + assert(bracket); + assert.equal(bracket.value, '[{c,d}]'); + }); + }); + + describe('invalid', () => { + it('should escape standalone closing braces', () => { + let one = parse('}'); + assert.equal(one.nodes[1].type, 'text'); + assert.equal(one.nodes[1].value, '}'); + + let two = parse('a}b'); + assert.equal(two.nodes[1].type, 'text'); + assert.equal(two.nodes[1].value, 'a}b'); + }); }); }); diff --git a/test/expanded.integration.js b/test/expanded.integration.js deleted file mode 100644 index dc664ae..0000000 --- a/test/expanded.integration.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} - -describe('integration', function() { - it('should work with dots in file paths', function() { - equal('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']); - equal('../{2..10..2}/../foo', ['../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo']); - equal('../{1..3}/../{a,b,c}/foo', ['../1/../a/foo', '../2/../a/foo', '../3/../a/foo', '../1/../b/foo', '../2/../b/foo', '../3/../b/foo', '../1/../c/foo', '../2/../c/foo', '../3/../c/foo']); - equal('./{a..z..3}/', ['./a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/']); - equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']); - }); - - it('should expand a complex combination of ranges and sets:', function() { - equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', 'a/y/5cdf.txt', 'a/x/1cef.txt', 'a/y/1cef.txt', 'a/x/2cef.txt', 'a/y/2cef.txt', 'a/x/3cef.txt', 'a/y/3cef.txt', 'a/x/4cef.txt', 'a/y/4cef.txt', 'a/x/5cef.txt', 'a/y/5cef.txt']); - }); - - it('should expand complex sets and ranges in `bash` mode:', function() { - equal('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/y/c{d}e']); - }); -}); diff --git a/test/expanded.ranges.js b/test/expanded.ranges.js deleted file mode 100644 index d118fa8..0000000 --- a/test/expanded.ranges.js +++ /dev/null @@ -1,193 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected) { - var actual = braces.expand(pattern).sort(); - assert.deepEqual(actual, expected.sort()); -} - -describe('expanded ranges', function() { - - // HEADS UP! If you're using the `--mm` flag minimatch freezes on these - describe('large numbers', function() { - it('should expand large numbers', function() { - equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); - }); - - it('should throw an error when range exceeds rangeLimit', function() { - assert.throws(function() { - braces.expand('{214748364..2147483649}'); - }); - }); - }); - - describe('escaping / invalid ranges', function() { - it('should not try to expand ranges with decimals', function() { - equal('{1.1..2.1}', ['{1.1..2.1}']); - equal('{1.1..~2.1}', ['{1.1..~2.1}']); - }); - - it('should escape invalid ranges:', function() { - equal('{1..0f}', ['{1..0f}']); - equal('{1..10..ff}', ['{1..10..ff}']); - equal('{1..10.f}', ['{1..10.f}']); - equal('{1..10f}', ['{1..10f}']); - equal('{1..20..2f}', ['{1..20..2f}']); - equal('{1..20..f2}', ['{1..20..f2}']); - equal('{1..2f..2}', ['{1..2f..2}']); - equal('{1..ff..2}', ['{1..ff..2}']); - equal('{1..ff}', ['{1..ff}']); - equal('{1.20..2}', ['{1.20..2}']); - }); - - it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() { - equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']); - equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); - }); - - it('should not expand quoted strings.', function() { - equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']); - equal('{"x,x"}', ['{x,x}']); - }); - - it('should escaped outer braces in nested non-sets', function() { - equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']); - equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']); - }); - - it('should escape imbalanced braces', function() { - equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); - equal('abc{', ['abc{']); - equal('{abc{', ['{abc{']); - equal('{abc', ['{abc']); - equal('}abc', ['}abc']); - equal('ab{c', ['ab{c']); - equal('ab{c', ['ab{c']); - equal('{{a,b}', ['{a', '{b']); - equal('{a,b}}', ['a}', 'b}']); - equal('abcd{efgh', ['abcd{efgh']); - equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']); - equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']); - equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']); - equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']); - equal('f{x,y{{g}h', ['f{x,y{{g}h']); - equal('f{x,y{{g}}h', ['f{x,y{{g}}h']); - equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']); - equal('f{x,y{}g}h', ['fxh', 'fy{}gh']); - equal('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']); - }); - }); - - describe('positive numeric ranges', function() { - it('should expand numeric ranges', function() { - equal('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']); - equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']); - equal('x{3..3}y', ['x3y']); - equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('{1..3}', ['1', '2', '3']); - equal('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']); - equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']); - equal('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']); - equal('{3..3}', ['3']); - equal('{5..8}', ['5', '6', '7', '8']); - }); - }); - - describe('negative ranges', function() { - it('should expand ranges with negative numbers', function() { - equal('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']); - equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']); - equal('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']); - equal('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']); - }); - }); - - describe('alphabetical ranges', function() { - it('should expand alphabetical ranges', function() { - equal('{a..F}', ['F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']); - equal('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']); - equal('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']); - equal('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']); - equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']); - equal('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']); - equal('{a..e}', ['a', 'b', 'c', 'd', 'e']); - equal('{A..E}', ['A', 'B', 'C', 'D', 'E']); - equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']); - equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']); - equal('{E..A}', ['E', 'D', 'C', 'B', 'A']); - equal('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']); - equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']); - equal('{f..f}', ['f']); - }); - - it('should expand multiple ranges:', function() { - equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']); - }); - }); - - describe('combo', function() { - it('should expand numerical ranges - positive and negative', function() { - equal('a{01..05}b', ['a01b', 'a02b', 'a03b', 'a04b', 'a05b' ]); - equal('0{1..9}/{10..20}', ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16', '05/17', '05/18', '05/19', '05/20', '06/10', '06/11', '06/12', '06/13', '06/14', '06/15', '06/16', '06/17', '06/18', '06/19', '06/20', '07/10', '07/11', '07/12', '07/13', '07/14', '07/15', '07/16', '07/17', '07/18', '07/19', '07/20', '08/10', '08/11', '08/12', '08/13', '08/14', '08/15', '08/16', '08/17', '08/18', '08/19', '08/20', '09/10', '09/11', '09/12', '09/13', '09/14', '09/15', '09/16', '09/17', '09/18', '09/19', '09/20' ]); - equal('{-10..10}', ['-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ]); - }); - }); - - describe('steps > positive ranges', function() { - it('should expand ranges using steps:', function() { - equal('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('{1..10..2}', ['1', '3', '5', '7', '9']); - equal('{1..20..20}', ['1']); - equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); - equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']); - equal('{10..1..2}', ['10', '8', '6', '4', '2']); - equal('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']); - equal('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('{2..10..2}', ['2', '4', '6', '8', '10']); - equal('{2..10..3}', ['2', '5', '8']); - equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); - }); - - it('should expand positive ranges with negative steps:', function() { - equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']); - }); - }); - - describe('steps > negative ranges', function() { - it('should expand negative ranges using steps:', function() { - equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']); - equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']); - equal('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']); - equal('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); - equal('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']); - equal('{-2..-10..3}', ['-2', '-5', '-8']); - equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); - equal('{10..1..-2}', ['2', '4', '6', '8', '10']); - equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']); - }); - }); - - describe('steps > alphabetical ranges', function() { - it('should expand alpha ranges with steps', function() { - equal('{a..e..2}', ['a', 'c', 'e']); - equal('{E..A..2}', ['E', 'C', 'A']); - equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']); - equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); - equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); - }); - - it('should expand alpha ranges with negative steps', function() { - equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); - }); - }); - - describe('padding', function() { - it('unwanted zero-padding -- fixed post-bash-4.0', function() { - equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']); - equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']); - equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); - }); - }); -}); diff --git a/test/expanded.sets.js b/test/expanded.sets.js deleted file mode 100644 index a3a0d0c..0000000 --- a/test/expanded.sets.js +++ /dev/null @@ -1,216 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected) { - var actual = braces.expand(pattern); - assert.deepEqual(actual.sort(), expected.sort()); -} - -describe('expanded sets', function() { - describe('invalid sets', function() { - it('should handle invalid sets:', function() { - equal('{0..10,braces}', ['0..10', 'braces']); - equal('{1..10,braces}', ['1..10', 'braces']); - }); - }); - - describe('extglobs', function() { - it('should split on commas when braces are inside extglobs', function() { - equal('*(a|{b|c,d})', ['*(a|b|c)', '*(a|d)']); - }); - - it('should not split on commas in extglobs when inside braces', function() { - equal('{a,@(b,c)}', ['a', '@(b,c)']); - equal('{a,*(b|c,d)}', ['a', '*(b|c,d)']); - }); - }); - - describe('escaping', function() { - it('should not expand escaped braces', function() { - equal('y{\\},a}x', ['y}x', 'yax']); - equal('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']); - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/b/c/{x,y\\}', ['a/b/c/{x,y}']); - equal('a/\\{x,y}/cde', ['a/{x,y}/cde']); - equal('abcd{efgh', ['abcd{efgh']); - equal('{abc}', ['{abc}']); - equal('{x,y,\\{a,b,c\\}}', ['x', 'y', '{a', 'b', 'c}']); - equal('{x,y,{a,b,c\\}}', ['{x,y,a', '{x,y,b', '{x,y,c}']); - equal('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']); - equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']); - }); - - it('should handle empty braces', function() { - equal('{ }', ['{ }']); - equal('{', ['{']); - equal('{}', ['{}']); - equal('}', ['}']); - }); - - it('should handle empty sets', function() { - equal('{ }', ['{ }']); - equal('{', ['{']); - equal('{}', ['{}']); - equal('}', ['}']); - }); - - it('should escape braces when only one value is defined', function() { - equal('a{b}c', ['a{b}c']); - equal('a/b/c{d}e', ['a/b/c{d}e']); - }); - - it('should escape closing braces when open is not defined', function() { - equal('{a,b}c,d}', ['ac,d}', 'bc,d}']); - }); - - it('should not expand braces in sets with es6/bash-like variables', function() { - equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']); - equal('a${b}c', ['a${b}c']); - equal('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']); - equal('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']); - }); - - it('should not expand escaped commas.', function() { - equal('a{b\\,c\\,d}e', ['a{b,c,d}e']); - equal('a{b\\,c}d', ['a{b,c}d']); - equal('{abc\\,def}', ['{abc,def}']); - equal('{abc\\,def,ghi}', ['abc,def', 'ghi']); - equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']); - }); - - it('should return sets with escaped commas', function() { - }); - - it('should not expand escaped braces.', function() { - equal('{a,b\\}c,d}', ['a', 'b}c', 'd']); - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/{z,\\{a,b,c,d,e}/d', ['a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d']); - equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']); - equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']); - }); - - it('should not expand escaped braces or commas.', function() { - equal('{x\\,y,\\{abc\\},trie}', ['{abc}', 'trie', 'x,y']); - }); - }); - - describe('multipliers', function() { - it('should support multipliers', function() { - equal('{{d,d},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']); - equal('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']); - equal('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']); - equal('a{,,}', ['a', 'a', 'a']); - equal('a{,}', ['a', 'a']); - equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a{,}{,}', ['a', 'a', 'a', 'a']); - equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']); - equal('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']); - equal('{,}', []); - equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']); - equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']); - equal('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']); - equal('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']); - equal('a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']); - equal('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']); - equal('a{,,}', ['a', 'a', 'a']); - equal('a{,}', ['a', 'a']); - equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); - equal('a{,}{,}', ['a', 'a', 'a', 'a']); - equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']); - equal('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']); - equal('{,}', []); - equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']); - equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']); - equal('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']); - }); - }); - - describe('set expansion', function() { - it('should support sequence brace operators', function() { - equal('{a,b,c}', ['a', 'b', 'c']); - equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - }); - - it('should support sequence braces with leading characters', function() { - equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex']); - equal('ff{c,b,a}', ['ffa', 'ffb', 'ffc']); - }); - - it('should support sequence braces with trailing characters', function() { - equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']); - equal('{l,n,m}xyz', ['lxyz', 'mxyz', 'nxyz']); - }); - - it('should support sequence braces with trailing characters', function() { - equal('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']); - equal('{{1..10..2},braces}', ['1', '3', '5', '7', '9', 'braces']); - equal('{{1..10},braces}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']); - }); - - it('should support nested sequence braces with trailing characters', function() { - equal('x{{0..10},braces}y', ['xbracesy', 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']); - }); - - it('should expand multiple sets', function() { - equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']); - equal('a{b,c}d{e,f}g', ['abdeg', 'abdfg', 'acdeg', 'acdfg']); - equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt']); - }); - - it('should expand nested sets', function() { - equal('{{d,d},e}{}', ['d{}', 'd{}', 'e{}']); - equal('{{d,d},e}a', ['da', 'da', 'ea']); - equal('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']); - equal('{d,d,{d,d},{e,e}}', ['d', 'd', 'd', 'd', 'e', 'e']); - equal('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']); - equal('{{d,d},e}', ['d', 'd', 'e']); - equal('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']); - equal('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']); - equal('a{{x,y},z}b', ['axb', 'ayb', 'azb']); - equal('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']); - equal('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'acex/z', 'ahx/z']); - equal('a{b,c{d,e},h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz']); - equal('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']); - equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']); - }); - - it('should expand with globs.', function() { - equal('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']); - equal('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']); - equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt']); - equal('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']); - }); - }); - - describe('commas', function() { - it('should work with leading and trailing commas.', function() { - equal('a{b,}c', ['abc', 'ac']); - equal('a{,b}c', ['abc', 'ac']); - equal('{{a,b},a}c', ['ac', 'ac', 'bc']); - equal('{{a,b},}c', ['ac', 'bc', 'c']); - equal('a{{a,b},}c', ['aac', 'abc', 'ac']); - }); - }); - - describe('spaces', function() { - it('should handle spaces', function() { - equal('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '05 17', '05 18', '05 19', '05 20', '06 10', '06 11', '06 12', '06 13', '06 14', '06 15', '06 16', '06 17', '06 18', '06 19', '06 20', '07 10', '07 11', '07 12', '07 13', '07 14', '07 15', '07 16', '07 17', '07 18', '07 19', '07 20', '08 10', '08 11', '08 12', '08 13', '08 14', '08 15', '08 16', '08 17', '08 18', '08 19', '08 20', '09 10', '09 11', '09 12', '09 13', '09 14', '09 15', '09 16', '09 17', '09 18', '09 19', '09 20']); - equal('a{ ,c{d, },h}x', ['acdx', 'ac x', 'ahx', 'a x']); - equal('a{ ,c{d, },h} ', ['a ', 'ac ', 'acd ', 'ah ']); - - // see https://github.com/jonschlinkert/micromatch/issues/66 - equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ]); - - // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`, - // That makes sense in Bash, since ' ' is a separator, but not here. - equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']); - }); - }); -}); diff --git a/test/minimatch.js b/test/minimatch.js index ba935c8..72a1182 100644 --- a/test/minimatch.js +++ b/test/minimatch.js @@ -1,27 +1,27 @@ 'use strict'; -var assert = require('assert'); -var braces = require('..'); +const assert = require('assert').strict; +const braces = require('..'); /** * minimatch v3.0.3 unit tests */ -describe('brace expansion', function() { - var units = [ +describe('brace expansion', () => { + const units = [ ['a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']], ['a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'] ], ['a{b}c', ['a{b}c']], ['a{00..05}b', ['a00b', 'a01b', 'a02b', 'a03b', 'a04b', 'a05b'] ], ['z{a,b},c}d', ['za,c}d', 'zb,c}d']], - ['z{a,b{,c}d', ['z{a,bcd', 'z{a,bd']], + ['z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']], ['a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']], ['a{b{c{d,e}f{x,y}}g}h', ['a{b{cdfx}g}h', 'a{b{cdfy}g}h', 'a{b{cefx}g}h', 'a{b{cefy}g}h'] ], ['a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh'] ] ]; - units.forEach(function(unit) { - it('should expand: ' + unit[0], function() { + units.forEach(unit => { + it('should expand: ' + unit[0], () => { assert.deepEqual(braces.expand(unit[0]), unit[1], unit[0]); }); }); diff --git a/test/multiples.js b/test/multiples.js index ff8b244..e433aa4 100644 --- a/test/multiples.js +++ b/test/multiples.js @@ -1,32 +1,31 @@ 'use strict'; -var assert = require('assert'); -var braces = require('..'); +const assert = require('assert').strict; +const braces = require('..'); -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} +const equal = (input, expected, options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; -describe('multiples', function() { - var patterns = [ +describe('multiples', () => { + const patterns = [ ['-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']], ['-v{,,,,}{,}', ['-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v']], ['a/b{,}', ['a/b', 'a/b']], ['a/{,}/b', ['a//b', 'a//b']], - ['a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']], - ['a/{a,b,{,}{,}{,},c}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b', 'a/c/b']], - ['a/{a,b,{,},c}/b', ['a//b', 'a//b', 'a/a/b', 'a/b/b', 'a/c/b']], - ['a/{a,b,{,}{,}{,}}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b']], - ['a/{b,cz{,}}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']], - ['a/{b,cz}{,}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']], + ['a/{,}{c,d}/e', ['a/c/e', 'a/d/e', 'a/c/e', 'a/d/e']], + ['a/{a,b,{,}{,}{,},c}/b', ['a/a/b', 'a/b/b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/c/b']], + ['a/{a,b,{,},c}/b', ['a/a/b', 'a/b/b', 'a//b', 'a//b', 'a/c/b']], + ['a/{a,b,{,}{,}{,}}/b', ['a/a/b', 'a/b/b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b']], + ['a/{b,cz{,}}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef']], + ['a/{b,cz}{,}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef']], ['a/{b,c{,}}', ['a/b', 'a/c', 'a/c']], ['a/{b,c{,}}/{,}', ['a/b/', 'a/b/', 'a/c/', 'a/c/', 'a/c/', 'a/c/']], ['a/{b,c}/{,}', ['a/b/', 'a/b/', 'a/c/', 'a/c/']], ['a/{b,c}{,}/d{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d']], - ['a/{b,c}{,}/{d,e{,}}', ['a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']], - ['a/{b,c}{,}/{d,e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']], - ['a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']], + ['a/{b,c}{,}/{d,e{,}}', ['a/b/d', 'a/b/e', 'a/b/e', 'a/b/d', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/d', 'a/c/e', 'a/c/e']], + ['a/{b,c}{,}/{d,e}{,}', ['a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e']], + ['a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e']], ['a/{c,d}/{x,y{,}}/e', ['a/c/x/e', 'a/c/y/e', 'a/c/y/e', 'a/d/x/e', 'a/d/y/e', 'a/d/y/e']], ['a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']], ['a{,,,,,}', ['a', 'a', 'a', 'a', 'a', 'a']], @@ -39,23 +38,21 @@ describe('multiples', function() { ['a{,,}{,,}{,,}{,}/b', ['a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b', 'a/b']], ['a{,,}{,}', ['a', 'a', 'a', 'a', 'a', 'a']], ['a{,}', ['a', 'a']], - ['a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']], + ['a{,}/{c,d}/e', ['a/c/e', 'a/d/e', 'a/c/e', 'a/d/e']], ['a{,}b', ['ab', 'ab']], ['a{,}{,}', ['a', 'a', 'a', 'a']], ['a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']], ['a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']], ['one/{a{,}{,}}/{b/c{,,}{,}{,,}{,}}/two', ['one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two']], - ['{,}', []], + ['{,}', ['', '']], ['{,}a/{,}', ['a/', 'a/', 'a/', 'a/']], - ['{,}{,}', []], + ['{,}{,}', ['', '', '', '']], ['{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']], ['{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']], ['{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']] ]; - patterns.forEach(function(pattern) { - it('should expand: ' + pattern[0], function() { - equal(pattern[0], pattern[1]); - }); + patterns.forEach(pattern => { + it('should expand: ' + pattern[0], () => equal(pattern[0], pattern[1])); }); }); diff --git a/test/optimized.js b/test/optimized.js deleted file mode 100644 index e11eb84..0000000 --- a/test/optimized.js +++ /dev/null @@ -1,418 +0,0 @@ -/*! - * braces - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License - */ - -'use strict'; - -require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - assert.deepEqual(braces(pattern, options), expected); -} - -describe('optimized', function() { - describe('sets', function() { - describe('invalid sets', function() { - it('should handle invalid sets:', function() { - equal('{0..10,braces}', ['(0..10|braces)']); - equal('{1..10,braces}', ['(1..10|braces)']); - }); - }); - - describe('extglobs', function() { - it('should not optimize when preceded by an extglob character', function() { - equal('a/@{b,c}/d', ['a/@b/d', 'a/@c/d']); - equal('a/!{b,c}/d', ['a/!b/d', 'a/!c/d']); - equal('a/*{b,c}/d', ['a/*b/d', 'a/*c/d']); - equal('a/+{b,c}/d', ['a/+b/d', 'a/+c/d']); - equal('a/?{b,c}/d', ['a/?b/d', 'a/?c/d']); - }); - - it('should not optimize when brace set contains extglobs', function() { - equal('{b,[F-Z],!([B-F])}.js', ['b.js', '[F-Z].js', '!([B-F]).js']); - }); - }); - - describe('escaping', function() { - it('should not expand escaped braces', function() { - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/b/c/{x,y\\}', ['a/b/c/{x,y}']); - equal('a/\\{x,y}/cde', ['a/{x,y}/cde']); - equal('abcd{efgh', ['abcd{efgh']); - equal('{abc}', ['{abc}']); - equal('{x,y,\\{a,b,c\\}}', ['(x|y|{a|b|c})']); - equal('{x,y,{a,b,c\\}}', ['{x,y,(a|b|c})']); - equal('{x,y,{abc},trie}', ['(x|y|{abc}|trie)']); - equal('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']); - }); - - it('should handle spaces', function() { - // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`, - // That makes sense in Bash, since ' ' is a separator, but not here. - equal('foo {1,2} bar', ['foo (1|2) bar']); - }); - - it('should handle empty braces', function() { - equal('{ }', ['{ }']); - equal('{', ['{']); - equal('{}', ['{}']); - equal('}', ['}']); - }); - - it('should escape braces when only one value is defined', function() { - equal('a{b}c', ['a{b}c']); - equal('a/b/c{d}e', ['a/b/c{d}e']); - }); - - it('should not expand braces in sets with es6/bash-like variables', function() { - equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']); - equal('a${b}c', ['a${b}c']); - equal('a/{${b},c}/d', ['a/(${b}|c)/d']); - equal('a${b,d}/{foo,bar}c', ['a${b,d}/(foo|bar)c']); - }); - - it('should not expand escaped commas.', function() { - equal('a{b\\,c\\,d}e', ['a{b,c,d}e']); - equal('a{b\\,c}d', ['a{b,c}d']); - equal('{abc\\,def}', ['{abc,def}']); - equal('{abc\\,def,ghi}', ['(abc,def|ghi)']); - equal('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']); - }); - - it('should return sets with escaped commas', function() { - equal('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']); - }); - - it('should not expand escaped braces.', function() { - equal('{a,b\\}c,d}', ['(a|b}c|d)']); - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']); - equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']); - equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']); - }); - - it('should not expand escaped braces or commas.', function() { - equal('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']); - }); - }); - - describe('set expansion', function() { - it('should support sequence brace operators', function() { - equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']); - equal('ff{c,b,a}', ['ff(c|b|a)']); - equal('f{d,e,f}g', ['f(d|e|f)g']); - equal('x{{0..10},braces}y', ['x(([0-9]|10)|braces)y']); - equal('{1..10}', ['([1-9]|10)']); - equal('{a,b,c}', ['(a|b|c)']); - equal('{braces,{0..10}}', ['(braces|([0-9]|10))']); - equal('{l,n,m}xyz', ['(l|n|m)xyz']); - equal('{{0..10},braces}', ['(([0-9]|10)|braces)']); - equal('{{1..10..2},braces}', ['((1|3|5|7|9)|braces)']); - equal('{{1..10},braces}', ['(([1-9]|10)|braces)']); - }); - - it('should expand multiple sets', function() { - equal('a/{a,b}/{c,d}/e', ['a/(a|b)/(c|d)/e']); - equal('a{b,c}d{e,f}g', ['a(b|c)d(e|f)g']); - equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/(x|y)/c(d|e)f.(md|txt)']); - }); - - it('should expand nested sets', function() { - equal('{a,b}{{a,b},a,b}', ['(a|b)((a|b)|a|b)']); - equal('a{b,c{d,e}f}g', ['a(b|c(d|e)f)g']); - equal('a{{x,y},z}b', ['a((x|y)|z)b']); - equal('f{x,y{g,z}}h', ['f(x|y(g|z))h']); - equal('a{b,c}{d,e}/hx/z', ['a(b|c)(d|e)/hx/z']); - equal('a{b,c{d,e},h}x/z', ['a(b|c(d|e)|h)x/z']); - equal('a{b,c{d,e},h}x{y,z}', ['a(b|c(d|e)|h)x(y|z)']); - equal('a{b,c{d,e},{f,g}h}x{y,z}', ['a(b|c(d|e)|(f|g)h)x(y|z)']); - equal('a-{b{d,e}}-c', ['a-{b(d|e)}-c']); - }); - - it('should expand not modify non-brace characters', function() { - equal('a/b/{d,e}/*.js', ['a/b/(d|e)/*.js']); - equal('a/**/c/{d,e}/f*.js', ['a/**/c/(d|e)/f*.js']); - equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/(d|e)/f*.(md|txt)']); - }); - }); - - describe('commas', function() { - it('should work with leading and trailing commas.', function() { - equal('a{b,}c', ['a(b|)c']); - equal('a{,b}c', ['a(|b)c']); - }); - }); - - describe('spaces', function() { - it('should handle spaces', function() { - equal('0{1..9} {10..20}', ['0([1-9]) (1[0-9]|20)']); - equal('a{ ,c{d, },h}x', ['a( |c(d| )|h)x']); - equal('a{ ,c{d, },h} ', ['a( |c(d| )|h) ']); - - // see https://github.com/jonschlinkert/microequal/issues/66 - equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']); - }); - }); - }); - - /** - * Ranges - */ - - describe('ranges', function() { - describe('escaping / invalid ranges', function() { - it('should not try to expand ranges with decimals', function() { - equal('{1.1..2.1}', ['{1.1..2.1}']); - equal('{1.1..~2.1}', ['{1.1..~2.1}']); - }); - - it('should escape invalid ranges:', function() { - equal('{1..0f}', ['{1..0f}']); - equal('{1..10..ff}', ['{1..10..ff}']); - equal('{1..10.f}', ['{1..10.f}']); - equal('{1..10f}', ['{1..10f}']); - equal('{1..20..2f}', ['{1..20..2f}']); - equal('{1..20..f2}', ['{1..20..f2}']); - equal('{1..2f..2}', ['{1..2f..2}']); - equal('{1..ff..2}', ['{1..ff..2}']); - equal('{1..ff}', ['{1..ff}']); - equal('{1..f}', ['([1-f])']); - equal('{1.20..2}', ['{1.20..2}']); - }); - - it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() { - equal('a-{b{d,e}}-c', ['a-{b(d|e)}-c']); - equal('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']); - }); - - it('should not expand quoted strings.', function() { - equal('{"klklkl"}{1,2,3}', ['{klklkl}(1|2|3)']); - equal('{"x,x"}', ['{x,x}']); - }); - - it('should escaped outer braces in nested non-sets', function() { - equal('{a-{b,c,d}}', ['{a-(b|c|d)}']); - equal('{a,{a-{b,c,d}}}', ['(a|{a-(b|c|d)})']); - }); - - it('should escape imbalanced braces', function() { - equal('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']); - equal('abc{', ['abc{']); - equal('{abc{', ['{abc{']); - equal('{abc', ['{abc']); - equal('}abc', ['}abc']); - equal('ab{c', ['ab{c']); - equal('{{a,b}', ['{(a|b)']); - equal('{a,b}}', ['(a|b)}']); - equal('abcd{efgh', ['abcd{efgh']); - equal('a{b{c{d,e}f}g}h', ['a(b(c(d|e)f)g)h']); - equal('f{x,y{{g,z}}h}', ['f(x|y((g|z))h)']); - equal('z{a,b},c}d', ['z(a|b),c}d']); - equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{c(d|e)f{x,y{{g}h']); - equal('f{x,y{{g}h', ['f{x,y{{g}h']); - equal('f{x,y{{g}}h', ['f{x,y{{g}}h']); - equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{c(d|e)f(x|y{}g)h']); - equal('f{x,y{}g}h', ['f(x|y{}g)h']); - equal('z{a,b{,c}d', ['z{a,b(|c)d']); - }); - }); - - describe('positive numeric ranges', function() { - it('should expand numeric ranges', function() { - equal('a{0..3}d', ['a([0-3])d']); - equal('x{10..1}y', ['x([1-9]|10)y']); - equal('x{3..3}y', ['x3y']); - equal('{1..10}', ['([1-9]|10)']); - equal('{1..3}', ['([1-3])']); - equal('{1..9}', ['([1-9])']); - equal('{10..1}', ['([1-9]|10)']); - equal('{10..1}y', ['([1-9]|10)y']); - equal('{3..3}', ['3']); - equal('{5..8}', ['([5-8])']); - }); - }); - - describe('negative ranges', function() { - it('should expand ranges with negative numbers', function() { - equal('{-1..-10}', ['(-[1-9]|-10)']); - equal('{-10..-1}', ['(-[1-9]|-10)']); - equal('{-20..0}', ['(-[1-9]|-1[0-9]|-20|0)']); - equal('{0..-5}', ['(-[1-5]|0)']); - equal('{9..-4}', ['(-[1-4]|[0-9])']); - }); - }); - - describe('alphabetical ranges', function() { - it('should expand alphabetical ranges', function() { - equal('0{1..9}/{10..20}', ['0([1-9])/(1[0-9]|20)']); - equal('0{a..d}0', ['0([a-d])0']); - equal('a/{b..d}/e', ['a/([b-d])/e']); - equal('{1..f}', ['([1-f])']); - equal('{a..A}', ['([A-a])']); - equal('{A..a}', ['([A-a])']); - equal('{a..e}', ['([a-e])']); - equal('{A..E}', ['([A-E])']); - equal('{a..f}', ['([a-f])']); - equal('{a..z}', ['([a-z])']); - equal('{E..A}', ['([A-E])']); - equal('{f..1}', ['([1-f])']); - equal('{f..a}', ['([a-f])']); - equal('{f..f}', ['f']); - }); - - it('should expand multiple ranges:', function() { - equal('a/{b..d}/e/{f..h}', ['a/([b-d])/e/([f-h])']); - }); - }); - - describe('combo', function() { - it('should expand numerical ranges - positive and negative', function() { - equal('{-10..10}', ['(-[1-9]|-?10|[0-9])']); - }); - }); - - // HEADS UP! If you're using the `--mm` flag minimatch freezes on these - describe('large numbers', function() { - it('should expand large numbers', function() { - equal('{2147483645..2147483649}', ['(214748364[5-9])']); - equal('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']); - }); - }); - - describe('steps > positive ranges', function() { - it('should expand ranges using steps:', function() { - equal('{1..10..1}', ['([1-9]|10)']); - equal('{1..10..2}', ['(1|3|5|7|9)']); - equal('{1..20..20}', ['1']); - equal('{1..20..20}', ['1']); - equal('{1..20..20}', ['1']); - equal('{1..20..2}', ['(1|3|5|7|9|11|13|15|17|19)']); - equal('{10..0..2}', ['(10|8|6|4|2|0)']); - equal('{10..1..2}', ['(10|8|6|4|2)']); - equal('{100..0..5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']); - equal('{2..10..1}', ['([2-9]|10)']); - equal('{2..10..2}', ['(2|4|6|8|10)']); - equal('{2..10..3}', ['(2|5|8)']); - equal('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']); - }); - - it('should expand positive ranges with negative steps:', function() { - equal('{10..0..-2}', ['(10|8|6|4|2|0)']); - }); - }); - - describe('steps > negative ranges', function() { - it('should expand negative ranges using steps:', function() { - equal('{-1..-10..-2}', ['(-(1|3|5|7|9))']); - equal('{-1..-10..2}', ['(-(1|3|5|7|9))']); - equal('{-10..-2..2}', ['(-(10|8|6|4|2))']); - equal('{-2..-10..1}', ['(-[2-9]|-10)']); - equal('{-2..-10..2}', ['(-(2|4|6|8|10))']); - equal('{-2..-10..3}', ['(-(2|5|8))']); - equal('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']); - equal('{-9..9..3}', ['(0|3|6|9|-(9|6|3))']); - equal('{10..1..-2}', ['(10|8|6|4|2)']); - equal('{100..0..-5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']); - }); - }); - - describe('steps > alphabetical ranges', function() { - it('should expand alpha ranges with steps', function() { - equal('{a..e..2}', ['(a|c|e)']); - equal('{E..A..2}', ['(E|C|A)']); - equal('{a..z}', ['([a-z])']); - equal('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']); - equal('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']); - }); - - it('should expand alpha ranges with negative steps', function() { - equal('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']); - }); - }); - - describe('padding', function() { - function isMatch(str, pattern) { - return braces.makeRe(pattern).test(str); - } - - it('should handled padded ranges', function() { - // 1..5 - assert(!isMatch('1', '{001..005}')); - assert(!isMatch('2', '{001..005}')); - assert(!isMatch('3', '{001..005}')); - assert(!isMatch('4', '{001..005}')); - assert(!isMatch('5', '{001..005}')); - - assert(isMatch('001', '{001..005}')); - assert(isMatch('002', '{001..005}')); - assert(isMatch('003', '{001..005}')); - assert(isMatch('004', '{001..005}')); - assert(isMatch('005', '{001..005}')); - - // 1..100 - assert(!isMatch('01', '{001..100}')); - assert(!isMatch('10', '{001..100}')); - assert(!isMatch('99', '{001..100}')); - assert(isMatch('001', '{001..100}')); - assert(isMatch('010', '{001..100}')); - assert(isMatch('099', '{001..100}')); - assert(isMatch('100', '{001..100}')); - - // -001..100 - assert(!isMatch('01', '{-0100..100}')); - assert(!isMatch('10', '{-0100..100}')); - assert(!isMatch('99', '{-0100..100}')); - assert(isMatch('-01', '{-0100..100}')); - assert(isMatch('-010', '{-0100..100}')); - assert(isMatch('-100', '{-0100..100}')); - assert(isMatch('-099', '{-0100..100}')); - assert(isMatch('100', '{-0100..100}')); - assert(isMatch('001', '{-0100..100}')); - assert(isMatch('010', '{-0100..100}')); - assert(isMatch('099', '{-0100..100}')); - assert(isMatch('100', '{-0100..100}')); - - assert(!isMatch('100', '{-001..-100}')); - assert(!isMatch('001', '{-001..-100}')); - assert(!isMatch('010', '{-001..-100}')); - assert(!isMatch('099', '{-001..-100}')); - assert(!isMatch('100', '{-001..-100}')); - assert(isMatch('-1', '{-001..-100}')); - assert(isMatch('-001', '{-001..-100}')); - assert(isMatch('-01', '{-001..-100}')); - assert(isMatch('-010', '{-001..-100}')); - assert(isMatch('-100', '{-001..-100}')); - assert(isMatch('-099', '{-001..-100}')); - }); - - it('unwanted zero-padding -- fixed post-bash-4.0', function() { - equal('{10..0..2}', ['(10|8|6|4|2|0)']); - equal('{10..0..-2}', ['(10|8|6|4|2|0)']); - equal('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']); - }); - }); - }); - - describe('integration', function() { - it('should work with dots in file paths', function() { - equal('../{1..3}/../foo', ['../([1-3])/../foo']); - equal('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo']); - equal('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo']); - equal('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/']); - equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']); - }); - - it('should expand a complex combination of ranges and sets:', function() { - equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']); - }); - - it('should expand complex sets and ranges in `bash` mode:', function() { - equal('a/{x,{1..5},y}/c{d}e', ['a/(x|([1-5])|y)/c{d}e']); - }); - }); -}); diff --git a/test/options.js b/test/options.js deleted file mode 100644 index b472b9f..0000000 --- a/test/options.js +++ /dev/null @@ -1,98 +0,0 @@ -/*! - * braces - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License - */ - -'use strict'; - -require('mocha'); -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - assert.deepEqual(braces(pattern, options).sort(), expected.sort()); -} - -describe('options', function() { - describe('options.expand', function() { - it('should expand braces when `options.expand` is true', function() { - equal('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true}); - }); - }); - - describe('options.cache', function() { - it('should disable caching', function() { - braces('a/{b,c}/d'); - assert(braces.cache.hasOwnProperty('a/{b,c}/d')); - braces('a/{b,c}/d'); - assert(braces.cache.hasOwnProperty('a/{b,c}/d')); - braces('a/{b,c}/d'); - assert(braces.cache.hasOwnProperty('a/{b,c}/d')); - braces('a/{b,c}/d', {cache: false}); - braces('a/{b,c}/d', {cache: false}); - braces('a/{b,c}/d', {cache: false}); - assert.deepEqual(braces.cache, {}); - }); - }); - - describe('options.noempty', function() { - it('should not remove empty values by default', function() { - equal('{,b{,a}}', ['', 'b', 'ba'], {expand: true}); - }); - - it('should remove empty values when `options.noempty` is false', function() { - equal('{,b{,a}}', ['b', 'ba'], {expand: true, noempty: true}); - }); - }); - - describe('options.nodupes', function() { - it('should not remove duplicates by default', function() { - equal('a/{b,b,b}/c', ['a/b/c', 'a/b/c', 'a/b/c'], {expand: true}); - }); - - it('should remove duplicates when `options.nodupes` is true', function() { - equal('a/{b,b,b}/c', ['a/b/c'], {expand: true, nodupes: true}); - }); - }); - - describe('options.optimize', function() { - it('should optimize braces when `options.optimize` is true', function() { - equal('a/{b,c}/d', ['a/(b|c)/d'], {optimize: true}); - }); - }); - - describe('options.quantifiers:', function() { - it('should not expand regex quantifiers when `options.quantifiers` is true', function() { - equal('a{2}c', ['a{2}c']); - equal('a{2}c', ['a{2}c'], {quantifiers: true}); - equal('a{2,}c', ['a{2,}c'], {quantifiers: true}); - equal('a{,2}c', ['a{,2}c'], {quantifiers: true}); - equal('a{2,3}c', ['a{2,3}c'], {quantifiers: true}); - }); - - it('should expand non-quantifiers when `options.quantifiers` is true', function() { - equal('a{2}c/{x,y}/z', ['a{2}c/(x|y)/z'], {quantifiers: true}); - equal('a{2}c/{x,y}/z', ['a{2}c/x/z', 'a{2}c/y/z'], {quantifiers: true, expand: true}); - }); - }); - - describe('options.unescape', function() { - it('should remove backslashes from escaped brace characters', function() { - equal('{a,b\\}c,d}', ['(a|b}c|d)']); - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']); - equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']); - equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']); - }); - - it('should not remove backslashes when `options.unescape` is false', function() { - equal('{a,b\\}c,d}', ['(a|b\\}c|d)'], {unescape: false}); - equal('\\{a,b,c,d,e}', ['\\{a,b,c,d,e}'], {unescape: false}); - equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|\\{a|b|c|d|e)/d'], {unescape: false}); - equal('a/\\{b,c}/{d,e}/f', ['a/\\{b,c}/(d|e)/f'], {unescape: false}); - equal('./\\{x,y}/{a..z..3}/', ['./\\{x,y}/(a|d|g|j|m|p|s|v|y)/'], {unescape: false}); - }); - }); -}); diff --git a/test/regression-1.8.5.js b/test/regression.js similarity index 69% rename from test/regression-1.8.5.js rename to test/regression.js index 00f652a..c9529a9 100644 --- a/test/regression-1.8.5.js +++ b/test/regression.js @@ -1,20 +1,15 @@ 'use strict'; -var assert = require('assert'); -var braces = require('..'); - -function equal(pattern, expected, options) { - options = options || {}; - var fn = braces; - if (options.optimize !== true) { - fn = braces.expand; - } - var actual = fn(pattern, options).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} - -describe('braces tests from 1.8.5', function() { - it('braces', function() { +require('mocha'); +const assert = require('assert').strict; +const braces = require('..'); + +const equal = (input, expected, options) => { + assert.deepEqual(braces.expand(input, options), expected); +}; + +describe('braces tests from 1.8.5', () => { + it('braces', () => { equal('ff{c,b,a}', ['ffc', 'ffb', 'ffa']); equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']); equal('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']); @@ -25,7 +20,7 @@ describe('braces tests from 1.8.5', function() { equal('{x,y,\\{a,b,c}}', ['x}', 'y}', '{a}', 'b}', 'c}']); equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']); - equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']); + equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/ucb/edit', '/usr/lib/ex', '/usr/lib/how_ex']); equal('{}', ['{}']); equal('{ }', ['{ }']); @@ -36,15 +31,15 @@ describe('braces tests from 1.8.5', function() { equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']); }); - it('new sequence brace operators', function() { + it('new sequence brace operators', () => { equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); equal('{0..10,braces}', ['0..10', 'braces']); equal('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - equal('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']); + equal('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']); + equal('x{{0..10},braces}y', ['x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y', 'xbracesy']); }); - it('ranges', function() { + it('ranges', () => { equal('{3..3}', ['3']); equal('x{3..3}y', ['x3y']); equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']); @@ -60,18 +55,18 @@ describe('braces tests from 1.8.5', function() { equal('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '05 17', '05 18', '05 19', '05 20', '06 10', '06 11', '06 12', '06 13', '06 14', '06 15', '06 16', '06 17', '06 18', '06 19', '06 20', '07 10', '07 11', '07 12', '07 13', '07 14', '07 15', '07 16', '07 17', '07 18', '07 19', '07 20', '08 10', '08 11', '08 12', '08 13', '08 14', '08 15', '08 16', '08 17', '08 18', '08 19', '08 20', '09 10', '09 11', '09 12', '09 13', '09 14', '09 15', '09 16', '09 17', '09 18', '09 19', '09 20']); }); - it('mixes are incorrectly-formed brace expansions', function() { + it('mixes are incorrectly-formed brace expansions', () => { // the first one is valid, but Bash fails on it equal('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']); - equal('{f..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']); + equal('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']); }); - it('do negative numbers work?', function() { + it('do negative numbers work?', () => { equal('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']); }); - it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() { + it('weirdly-formed brace expansions -- fixed in post-bash-3.1', () => { equal('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']); equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']); @@ -82,7 +77,7 @@ describe('braces tests from 1.8.5', function() { equal('{"x,x"}', ['{x,x}']); }); - it('numerical ranges with steps', function() { + it('numerical ranges with steps', () => { equal('{1..10..2}', ['1', '3', '5', '7', '9']); equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']); equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']); @@ -97,23 +92,23 @@ describe('braces tests from 1.8.5', function() { equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']); }); - it('alpha ranges with steps', function() { + it('alpha ranges with steps', () => { equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']); equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); }); - it('make sure brace expansion handles ints > 2**31 - 1 using intmax_t', function() { + it('make sure brace expansion handles ints > 2**31 - 1 using intmax_t', () => { equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); }); - it('unwanted zero-padding -- fixed post-bash-4.0', function() { + it('unwanted zero-padding -- fixed post-bash-4.0', () => { equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']); equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']); equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); }); - it('bad', function() { + it('bad', () => { equal('{1..10.f}', ['{1..10.f}']); equal('{1..ff}', ['{1..ff}']); equal('{1..10..ff}', ['{1..10..ff}']); @@ -130,13 +125,13 @@ describe('braces tests from 1.8.5', function() { }); }); -describe('bash tests', function() { - describe('brace expansion', function() { - it('should return an empty array when no braces are found', function() { - equal('', ['']); +describe('bash tests', () => { + describe('brace expansion', () => { + it('should return an empty array when no braces are found', () => { + equal('', []); }); - it('should expand emty sets', function() { + it('should expand emty sets', () => { equal('a{,}', ['a', 'a']); equal('{,}b', ['b', 'b']); equal('a{,}b', ['ab', 'ab']); @@ -144,51 +139,51 @@ describe('bash tests', function() { equal('a{,}{,}', ['a', 'a', 'a', 'a']); equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']); equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']); - equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']); + equal('a{,}/{c,d}/e', ['a/c/e', 'a/d/e', 'a/c/e', 'a/d/e']); equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']); }); - it('should eliminate dupes in repeated strings', function() { - equal('a{,}', ['a'], {nodupes: true}); - equal('a{,}{,}', ['a'], {nodupes: true}); - equal('a{,}{,}{,}', ['a'], {nodupes: true}); - equal('{a,b{,}{,}{,}}', ['a', 'b'], {nodupes: true}); - equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true}); - equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true}); + it('should eliminate dupes in repeated strings', () => { + equal('a{,}', ['a'], { nodupes: true }); + equal('a{,}{,}', ['a'], { nodupes: true }); + equal('a{,}{,}{,}', ['a'], { nodupes: true }); + equal('{a,b{,}{,}{,}}', ['a', 'b'], { nodupes: true }); + equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], { nodupes: true }); + equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], { nodupes: true }); }); - it('should work with no braces', function() { + it('should work with no braces', () => { equal('abc', ['abc']); }); - it('should work with no commas', function() { + it('should work with no commas', () => { equal('a{b}c', ['a{b}c']); }); - it('should work with no commas in `bash` mode', function() { + it('should work with no commas in `bash` mode', () => { equal('a{b}c', ['a{b}c']); }); - it('should handle spaces', function() { + it('should handle spaces', () => { equal('a{ ,c{d, },h}x', ['a x', 'acdx', 'ac x', 'ahx']); equal('a{ ,c{d, },h} ', [ 'a ', 'acd ', 'ac ', 'ah ' ]); - // see https://github.com/jonschlinkert/microequal/issues/66 + // see https://github.com/jonschlinkert/micromatch/issues/66 equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs' ]); }); - it('should handle empty braces', function() { + it('should handle empty braces', () => { equal('{ }', ['{ }']); equal('{}', ['{}']); equal('}', ['}']); equal('{', ['{']); - equal('{,}', []); + equal('{,}', ['', '']); }); - it('should handle imbalanced braces', function() { + it('should handle imbalanced braces', () => { equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); equal('abc{', ['abc{']); equal('{abc{', ['{abc{']); @@ -205,12 +200,12 @@ describe('bash tests', function() { equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']); equal('f{x,y{{g}h', ['f{x,y{{g}h']); equal('f{x,y{{g}}h', ['f{x,y{{g}}h']); - equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cefxh', 'a{b{cdfy{}gh', 'a{b{cefy{}gh']); + equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']); equal('f{x,y{}g}h', ['fxh', 'fy{}gh']); equal('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']); }); - it('should handle invalid braces in `bash mode`:', function() { + it('should handle invalid braces in `bash mode`:', () => { equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']); equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']); equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']); @@ -219,47 +214,47 @@ describe('bash tests', function() { equal('f{x,y{{g}}h', ['f{x,y{{g}}h']); }); - it('should return invalid braces:', function() { + it('should return invalid braces:', () => { equal('{0..10,braces}', ['0..10', 'braces']); }); - it('should not expand quoted strings.', function() { + it('should not expand quoted strings.', () => { equal('{"x,x"}', ['{x,x}']); equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']); }); - it('should work with one value', function() { + it('should work with one value', () => { equal('a{b}c', ['a{b}c']); equal('a/b/c{d}e', ['a/b/c{d}e']); }); - it('should work with one value in `bash` mode', function() { + it('should work with one value in `bash` mode', () => { equal('a{b}c', ['a{b}c']); equal('a/b/c{d}e', ['a/b/c{d}e']); }); - it('should work with nested non-sets', function() { + it('should work with nested non-sets', () => { equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']); equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']); equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']); }); - it('should work with nested non-sets in `bash` mode', function() { + it('should work with nested non-sets in `bash` mode', () => { equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']); equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']); }); - it('should not expand dots with leading slashes (escaped or paths).', function() { + it('should not expand dots with leading slashes (escaped or paths).', () => { equal('a{b,c/*/../d}e', ['abe', 'ac/*/../de']); equal('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd']); }); - it('should work with commas.', function() { + it('should work with commas.', () => { equal('a{b,}c', ['abc', 'ac']); equal('a{,b}c', ['ac', 'abc']); }); - it('should expand sets', function() { + it('should expand sets', () => { equal('a/{x,y}/cde', ['a/x/cde', 'a/y/cde']); equal('a/b/c/{x,y}', ['a/b/c/x', 'a/b/c/y']); equal('ff{c,b,a}', ['ffc', 'ffb', 'ffa']); @@ -268,46 +263,46 @@ describe('bash tests', function() { equal('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']); }); - it('should expand multiple sets', function() { - equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/b/c/e', 'a/a/d/e', 'a/b/d/e']); - equal('a{b,c}d{e,f}g', ['abdeg', 'acdeg', 'abdfg', 'acdfg']); - equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/y/cdf.md', 'a/x/cef.md', 'a/y/cef.md', 'a/x/cdf.txt', 'a/y/cdf.txt', 'a/x/cef.txt', 'a/y/cef.txt']); + it('should expand multiple sets', () => { + equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']); + equal('a{b,c}d{e,f}g', ['abdeg', 'abdfg', 'acdeg', 'acdfg']); + equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt']); }); - it('should expand nested sets', function() { + it('should expand nested sets', () => { equal('a/{b,c,{d,e}}/g', ['a/b/g', 'a/c/g', 'a/d/g', 'a/e/g']); equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']); - equal('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']); - equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']); + equal('{a,b}{{a,b},a,b}', ['aa', 'ab', 'aa', 'ab', 'ba', 'bb', 'ba', 'bb']); + equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/ucb/edit', '/usr/lib/ex', '/usr/lib/how_ex']); equal('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']); - equal('a{{x,y},z}b', ['axb', 'azb', 'ayb']); + equal('a{{x,y},z}b', ['axb', 'ayb', 'azb']); equal('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']); - equal('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'ahx/z', 'acex/z']); - equal('a{b,c{d,e},h}x{y,z}', ['abxy', 'acdxy', 'ahxy', 'acexy', 'abxz', 'acdxz', 'ahxz', 'acexz']); - equal('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'acdxy', 'afhxy', 'acexy', 'aghxy', 'abxz', 'acdxz', 'afhxz', 'acexz', 'aghxz']); + equal('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'acex/z', 'ahx/z']); + equal('a{b,c{d,e},h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz']); + equal('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']); equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']); }); - it('should expand with globs.', function() { + it('should expand with globs.', () => { equal('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']); equal('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']); - equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/e/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.txt']); + equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt']); }); - it('should expand with extglobs.', function() { + it('should expand with extglobs.', () => { equal('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']); }); }); - describe('escaping:', function() { - it('should not expand strings with es6/bash-like variables.', function() { + describe('escaping:', () => { + it('should not expand strings with es6/bash-like variables.', () => { equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']); equal('a${b}c', ['a${b}c']); equal('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']); equal('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']); }); - it('should not expand escaped commas.', function() { + it('should not expand escaped commas.', () => { equal('a{b\\,c}d', ['a{b,c}d']); equal('a{b\\,c\\,d}e', ['a{b,c,d}e']); equal('{abc\\,def}', ['{abc,def}']); @@ -315,36 +310,25 @@ describe('bash tests', function() { equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']); }); - it('should return sets with escaped commas in `bash` mode.', function() { + it('should return sets with escaped commas in `bash` mode.', () => { equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']); }); - it('should not expand escaped braces.', function() { + it('should not expand escaped braces.', () => { equal('{a,b\\}c,d}', ['a', 'b}c', 'd']); equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - equal('a/{b,\\{a,b,c,d,e}/d', ['a/b/d', 'a/b/d', 'a/{a/d', 'a/c/d', 'a/d/d', 'a/e/d']); + equal('a/{b,\\{a,b,c,d,e}/d', ['a/b/d', 'a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d']); equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']); - equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true}); }); - it('should not expand escaped braces or commas.', function() { + it('should not expand escaped braces or commas.', () => { equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']); }); }); - - describe('complex', function() { - it('should expand a complex combination of ranges and sets:', function() { - equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', 'a/y/5cdf.txt', 'a/x/1cef.txt', 'a/y/1cef.txt', 'a/x/2cef.txt', 'a/y/2cef.txt', 'a/x/3cef.txt', 'a/y/3cef.txt', 'a/x/4cef.txt', 'a/y/4cef.txt', 'a/x/5cef.txt', 'a/y/5cef.txt']); - }); - - it('should expand complex sets and ranges in `bash` mode:', function() { - equal('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/y/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e']); - }); - }); }); -describe('range expansion', function() { - it('should expand numerical ranges', function() { +describe('range expansion', () => { + it('should expand numerical ranges', () => { equal('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']); equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']); equal('x{3..3}y', ['x3y']); @@ -357,7 +341,7 @@ describe('range expansion', function() { equal('{5..8}', ['5', '6', '7', '8']); }); - it('should expand alphabetical ranges', function() { + it('should expand alphabetical ranges', () => { equal('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']); equal('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']); equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']); @@ -371,41 +355,31 @@ describe('range expansion', function() { equal('{f..f}', ['f']); }); - it('should use steps with alphabetical ranges', function() { + it('should use steps with alphabetical ranges', () => { equal('{a..e..2}', ['a', 'c', 'e']); equal('{E..A..2}', ['E', 'C', 'A']); }); - it('should not try to expand ranges with decimals', function() { + it('should not try to expand ranges with decimals', () => { equal('{1.1..2.1}', ['{1.1..2.1}']); - equal('{1.1..2.1}', ['{1.1..2.1}'], {optimize: true}); - equal('{1.1..~2.1}', ['{1.1..~2.1}'], {optimize: true}); }); - it('should expand negative ranges', function() { + it('should expand negative ranges', () => { equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']); equal('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']); equal('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']); equal('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']); }); - it('should expand multiple ranges:', function() { - equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/c/e/f', 'a/d/e/f', 'a/b/e/g', 'a/c/e/g', 'a/d/e/g', 'a/b/e/h', 'a/c/e/h', 'a/d/e/h']); + it('should expand multiple ranges:', () => { + equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']); }); - it('should work with dots in file paths', function() { + it('should work with dots in file paths', () => { equal('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']); }); - it('should make a regex-string when `options.optimize` is defined:', function() { - equal('../{1..3}/../foo', ['../([1-3])/../foo'], {optimize: true}); - equal('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo'], {optimize: true}); - equal('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo'], {optimize: true}); - equal('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/'], {optimize: true}); - equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true}); - }); - - it('should expand ranges using steps:', function() { + it('should expand ranges using steps:', () => { equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']); equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']); equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); @@ -432,7 +406,7 @@ describe('range expansion', function() { equal('{2..10..3}', ['2', '5', '8']); }); - it('should expand negative ranges using steps:', function() { + it('should expand negative ranges using steps:', () => { equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']); equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']); equal('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']); @@ -442,13 +416,13 @@ describe('range expansion', function() { equal('{-9..9..3}', ['-9', '-6', '-3', '0', '3', '6', '9']); }); - it('should expand mixed ranges and sets:', function() { - equal('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']); - equal('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + it('should expand mixed ranges and sets:', () => { + equal('x{{0..10},braces}y', ['x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y', 'xbracesy']); + equal('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']); equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); }); - it('should return invalid ranges:', function() { + it('should return invalid ranges:', () => { equal('{1.20..2}', ['{1.20..2}']); equal('{1..0f}', ['{1..0f}']); equal('{1..10..ff}', ['{1..10..ff}']); diff --git a/test/support/bash.js b/test/support/bash.js deleted file mode 100644 index b06c73e..0000000 --- a/test/support/bash.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -var isWindows = require('is-windows'); -var spawn = require('cross-spawn'); -var utils = require('./utils'); - -/** - * Expose `bash` util - */ - -module.exports = function(pattern) { - if (isWindows()) { - throw new Error('windows not supported'); - } - - var cmd = pattern; - if (!/echo/.test(cmd)) { - cmd = 'echo ' + escape(pattern); - } - - var res = spawn.sync(utils.getBashPath(), ['-c', cmd]); - var err = res.stderr && res.stderr.toString().trim(); - if (err) { - console.error(cmd); - throw new Error(err); - } - - if (!res.stdout) { - return []; - } - return unescape(res.stdout).sort(); -}; - -/** - * Escape characters that behave differently in bash than node (like spaces, which are - * valid path characters in node.js but indicate a delimiter in Bash) - */ - -function escape(buf) { - return buf.split(/\\? /).join('_SPACE_') - .replace(/([*`\[\]])/g, '\\$1') - .replace(/(\$\{)([^{}]+)(\})/g, function(m, $1, $2, $3) { - return utils.nc[0] + $2 + utils.nc[2]; - }); -} - -/** - * Unescape previously-escaped characters - */ - -function unescape(buf) { - return buf.toString().split(/[ \n]/) - .filter(Boolean) - .map(function(str) { - return utils.unescape(str, {escape: true}) - .split('_SPACE_').join(' ') - .split(/\\(?!`)/).join(''); - }); -} diff --git a/test/support/generate.js b/test/support/generate.js deleted file mode 100644 index f892749..0000000 --- a/test/support/generate.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict'; - -var braces = require('../..'); -var mm = require('minimatch'); -var text = require('text-table'); -var Time = require('time-diff'); -var time = new Time(); - -var table = [ - ['**Pattern**', '**braces**', '**minimatch**'], - ['---', '---', '---'] -]; - -// warm up both libs -mm.braceExpand('{a,b}'); -braces('{a,b}'); - -function generate(pattern) { - time.start('braces'); - var bval = braces(pattern, {rangeLimit: false}).join('|'); - var b = [wrap(format(bval.length)), '(' + time.end('braces', 'μs') + ')'].join(' '); - - time.start('minimatch'); - var mval = mm.braceExpand(pattern).join('|'); - var m = [wrap(format(mval.length)), '(' + time.end('minimatch', 'μs') + ')'].join(' '); - - table.push([wrap(pattern), b, m]); - return table; -} - -function wrap(str) { - return '`' + str + '`'; -} - -var patterns = [ - // '{1..9007199254740991}', - // '{1..1000000000000000}', - // '{1..100000000000000}', - // '{1..10000000000000}', - // '{1..1000000000000}', - // '{1..100000000000}', - // '{1..10000000000}', - // '{1..1000000000}', - // '{1..100000000}', - '{1..10000000}', - '{1..1000000}', - '{1..100000}', - '{1..10000}', - '{1..1000}', - '{1..100}', - '{1..10}', - '{1..3}', - // '/some/file/path/id-{0001..2017}', - // '/some/file/path/id-{0100..2017}', - // '/some/file/path/id-{1000..2017}', - // '/some/file/path/id-{1900..2017}', - // '/some/file/path/id-{2000..2017}', -]; - -for (var i = 0; i < patterns.length; i++) { - generate(patterns[i]); -} - -console.log(text(table, {hsep: ' | '})); - -function format(number, precision) { - if (typeof precision !== 'number') { - precision = 2; - } - - var abbr = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - precision = Math.pow(10, precision); - number = Number(number); - - var len = abbr.length - 1; - while (len-- >= 0) { - var size = Math.pow(10, len * 3); - if (size <= (number + 1)) { - number = Math.round(number * precision / size) / precision; - number += ' ' + abbr[len]; - break; - } - } - return number; -} - diff --git a/test/support/utils.js b/test/support/utils.js deleted file mode 100644 index 21be11a..0000000 --- a/test/support/utils.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var util = require('util'); -var bashPath = ''; - -/** - * Utils - */ - -exports.extend = require('extend-shallow'); -exports.nc = require('noncharacters'); - -exports.getBashPath = function() { - if (bashPath) return bashPath; - if (fs.existsSync('/usr/local/bin/bash')) { - bashPath = '/usr/local/bin/bash'; - } else if (fs.existsSync('/bin/bash')) { - bashPath = '/bin/bash'; - } else { - bashPath = 'bash'; - } - return bashPath; -}; - -exports.escape = function(str, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: ' + util.inspect(str)); - } - var opts = exports.extend({}, options); - if (!opts.expand && !opts.escape) return str; - str = str.replace(/(\$\{([^{}]+?)\})/g, function(m, $1, $2) { - return exports.nc[0] + $2 + exports.nc[2]; - }); - str = str.replace(/(\{)([^{,.}]+?)(\})/g, function(m, $1, $2, $3) { - return exports.nc[1] + $2 + exports.nc[2]; - }); - str = str.replace(/\\\{|\{(?!.*\})/g, exports.nc[1]); - str = str.replace(/\\\}/g, exports.nc[2]); - str = str.replace(/\\\,/g, exports.nc[3]); - if (!/\{/.test(str)) { - return str.replace(/\}/g, exports.nc[2]); - } - return str; -}; - -exports.unescape = function(str, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: ' + util.inspect(str)); - } - var opts = exports.extend({}, options); - if (!opts.expand && !opts.escape) return str; - var pre = opts.noescape ? '' : '\\'; - str = str.split(exports.nc[0]).join(pre ? '\\$\\{' : '${'); - str = str.split(exports.nc[1]).join(pre + '{'); - str = str.split(exports.nc[2]).join(pre + '}'); - str = str.split(exports.nc[3]).join(','); - return str.replace(/\\+/g, '\\'); -}; diff --git a/test/utils.js b/test/utils.js deleted file mode 100644 index c2afd72..0000000 --- a/test/utils.js +++ /dev/null @@ -1,78 +0,0 @@ -/*! - * braces - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License - */ - -'use strict'; - -require('mocha'); -var assert = require('assert'); -var utils = require('../lib/utils'); - -describe('utils', function() { - describe('.isEmptySets', function() { - it('should return true if string contains only empty stems', function() { - assert(utils.isEmptySets('{,}')); - assert(utils.isEmptySets('{,}{,}')); - assert(utils.isEmptySets('{,}{,}{,}{,}{,}')); - }); - - it('should return false if string contains more than empty stems', function() { - assert(!utils.isEmptySets('{,}foo')); - }); - - it('should return false if string contains other than empty stems', function() { - assert(!utils.isEmptySets('foo')); - }); - }); - - describe('.split', function() { - it('should split on commas by default', function() { - assert.deepEqual(utils.split('a,b,c'), ['a', 'b', 'c']); - assert.deepEqual(utils.split('{a,b,c}'), ['{a', 'b', 'c}']); - }); - - it('should not split inside parens', function() { - assert.deepEqual(utils.split('*(a|{b|c,d})'), ['*(a|{b|c,d})']); - assert.deepEqual(utils.split('a,@(b,c)'), ['a', '@(b,c)']); - assert.deepEqual(utils.split('a,*(b|c,d),z'), ['a', '*(b|c,d)', 'z']); - }); - - it('should work with unclosed parens', function() { - assert.deepEqual(utils.split('*(a|{b|c,d}'), ['*(a|{b|c,d}']); - }); - - it('should not split inside nested parens', function() { - assert.deepEqual(utils.split('a,*(b|(c,d)),z'), ['a', '*(b|(c,d))', 'z']); - assert.deepEqual(utils.split('a,*(b,(c,d)),z'), ['a', '*(b,(c,d))', 'z']); - }); - - it('should not split inside brackets', function() { - assert.deepEqual(utils.split('[a-z,"]*'), ['[a-z,"]*']); - }); - - it('should work with unclosed brackets', function() { - assert.deepEqual(utils.split('[a-z,"*'), ['[a-z,"*']); - }); - - it('should not split parens nested inside brackets', function() { - assert.deepEqual(utils.split('[-a(z,")]*'), ['[-a(z,")]*']); - }); - - it('should not split brackets nested inside parens', function() { - assert.deepEqual(utils.split('x,(a,[-a,])*'), ['x', '(a,[-a,])*']); - assert.deepEqual(utils.split('a,(1,[^(x,y)],3),z'), ['a', '(1,[^(x,y)],3)', 'z']); - }); - - it('should support escaped parens', function() { - assert.deepEqual(utils.split('a,@(b,c\\),z)'), ['a', '@(b,c\\),z)']); - }); - - it('should support escaped brackets', function() { - assert.deepEqual(utils.split('a,@([b,c\\],x]|b),z'), ['a', '@([b,c\\],x]|b)', 'z']); - assert.deepEqual(utils.split('a,@([b,c\\],x],b),z'), ['a', '@([b,c\\],x],b)', 'z']); - }); - }); -});