From 92ec96db3b04bd57adbc27e2a41a9bed79c96dbb Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Sun, 31 Mar 2019 15:15:24 -0400 Subject: [PATCH] start refactoring --- backup.js | 318 ++++++++++++ bench/index.js | 78 +++ bench/package.json | 21 + benchmark/code/brace-expansion.js | 4 - benchmark/code/braces.js | 4 - benchmark/code/minimatch.js | 4 - benchmark/fixtures/combination-nested.js | 1 - benchmark/fixtures/combination.js | 1 - benchmark/fixtures/escaped.js | 1 - benchmark/fixtures/list-basic.js | 1 - benchmark/fixtures/list-multiple.js | 1 - benchmark/fixtures/match.multiple.js | 1 - benchmark/fixtures/match.sequence.js | 1 - benchmark/fixtures/no-braces.js | 1 - benchmark/fixtures/sequence-basic.js | 1 - benchmark/fixtures/sequence-multiple.js | 1 - benchmark/index.js | 30 -- benchmark/last.md | 65 --- gulpfile.js | 32 -- index.js | 316 +----------- lib/braces.js | 104 ---- lib/compile.js | 20 + lib/compilers.js | 282 ----------- lib/constants.js | 59 +++ lib/expand.js | 126 +++++ lib/parse.js | 300 ++++++++++++ lib/parsers.js | 360 -------------- lib/stringify.js | 20 + lib/utils.js | 343 ------------- package.json | 31 +- ...sh.expanded.js => bash-expanded-braces.js} | 46 +- test/bash.optimized.js | 410 ---------------- test/bash.spec.js | 189 ------- test/brace-expansion.js | 91 ---- test/braces.compile.js | 60 ++- test/braces.create.js | 21 - test/braces.expand.js | 13 + test/braces.js | 146 ------ test/braces.makeRe.js | 21 - test/braces.parse.js | 44 +- test/expanded.integration.js | 27 - test/expanded.ranges.js | 193 -------- test/expanded.sets.js | 216 -------- test/minimatch.js | 28 -- test/multiples.js | 61 --- test/optimized.js | 418 ---------------- test/options.js | 98 ---- test/regression-1.8.5.js | 463 ------------------ test/support/bash.js | 59 --- test/support/generate.js | 86 ---- test/support/utils.js | 59 --- test/utils.js | 78 --- 52 files changed, 1082 insertions(+), 4272 deletions(-) create mode 100644 backup.js create mode 100644 bench/index.js create mode 100644 bench/package.json delete mode 100644 benchmark/code/brace-expansion.js delete mode 100644 benchmark/code/braces.js delete mode 100644 benchmark/code/minimatch.js delete mode 100644 benchmark/fixtures/combination-nested.js delete mode 100644 benchmark/fixtures/combination.js delete mode 100644 benchmark/fixtures/escaped.js delete mode 100644 benchmark/fixtures/list-basic.js delete mode 100644 benchmark/fixtures/list-multiple.js delete mode 100644 benchmark/fixtures/match.multiple.js delete mode 100644 benchmark/fixtures/match.sequence.js delete mode 100644 benchmark/fixtures/no-braces.js delete mode 100644 benchmark/fixtures/sequence-basic.js delete mode 100644 benchmark/fixtures/sequence-multiple.js delete mode 100644 benchmark/index.js delete mode 100644 benchmark/last.md delete mode 100644 gulpfile.js delete mode 100644 lib/braces.js create mode 100644 lib/compile.js delete mode 100644 lib/compilers.js create mode 100644 lib/constants.js create mode 100644 lib/expand.js create mode 100644 lib/parse.js delete mode 100644 lib/parsers.js create mode 100644 lib/stringify.js delete mode 100644 lib/utils.js rename test/{bash.expanded.js => bash-expanded-braces.js} (96%) delete mode 100644 test/bash.optimized.js delete mode 100644 test/bash.spec.js delete mode 100644 test/brace-expansion.js delete mode 100644 test/braces.create.js create mode 100644 test/braces.expand.js delete mode 100644 test/braces.js delete mode 100644 test/braces.makeRe.js delete mode 100644 test/expanded.integration.js delete mode 100644 test/expanded.ranges.js delete mode 100644 test/expanded.sets.js delete mode 100644 test/minimatch.js delete mode 100644 test/multiples.js delete mode 100644 test/optimized.js delete mode 100644 test/options.js delete mode 100644 test/regression-1.8.5.js delete mode 100644 test/support/bash.js delete mode 100644 test/support/generate.js delete mode 100644 test/support/utils.js delete mode 100644 test/utils.js diff --git a/backup.js b/backup.js new file mode 100644 index 0000000..c2eb828 --- /dev/null +++ b/backup.js @@ -0,0 +1,318 @@ +'use strict'; + +/** + * Module dependencies + */ + +const toRegex = require('to-regex'); +const unique = require('array-unique'); +const extend = require('extend-shallow'); + +/** + * Local dependencies + */ + +const compilers = require('./lib/compilers'); +const parsers = require('./lib/parsers'); +const Braces = require('./lib/braces'); +const utils = require('./lib/utils'); +const MAX_LENGTH = 1024 * 64; +let cache = {}; + +/** + * 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)). + * + * ```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'] + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {String} + * @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]; + } + + if (Array.isArray(pattern)) { + for (var i = 0; i < pattern.length; i++) { + arr.push.apply(arr, braces.create(pattern[i], options)); + } + } else { + arr = braces.create(pattern, options); + } + + if (options && options.nodupes === true) { + arr = unique(arr); + } + + if (!disabled) { + cache[key] = arr; + } + return arr; +} + +/** + * 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. + * + * ```js + * var braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/b/d', 'a/c/d']; + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ + +braces.expand = function(pattern, options) { + return braces.create(pattern, extend({}, options, { expand: true })); +}; + +/** + * Expands 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.expand('a/{b,c}/d')); + * //=> ['a/(b|c)/d'] + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ + +braces.optimize = function(pattern, options) { + return braces.create(pattern, 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'); + * 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 {String} `pattern` Brace pattern + * @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'); + } + + 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); +}; + +/** + * Create a regular expression from the given string `pattern`. + * + * ```js + * var braces = require('braces'); + * + * console.log(braces.makeRe('id-{200..300}')); + * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/ + * ``` + * @param {String} `pattern` The pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ + +braces.makeRe = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } + + var maxLength = (options && options.maxLength) || MAX_LENGTH; + if (pattern.length >= maxLength) { + throw new Error('expected pattern to be less than ' + maxLength + ' characters'); + } + + function makeRe() { + var arr = braces(pattern, options); + var opts = extend({strictErrors: false}, options); + return toRegex(arr, opts); + } + + 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); +}; + +/** + * Compile the given `ast` or string with the given `options`. + * + * ```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: [] } + * ``` + * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first. + * @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(); + * ``` + * @api public + */ + +braces.clearCache = function() { + cache = braces.cache = {}; +}; + +/** + * 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. + */ + +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); + } + + if (cache.hasOwnProperty(key)) { + return cache[key]; + } + + 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; + +/** + * Expose `braces` + * @type {Function} + */ + +module.exports = braces; diff --git a/bench/index.js b/bench/index.js new file mode 100644 index 0000000..123122d --- /dev/null +++ b/bench/index.js @@ -0,0 +1,78 @@ +'use strict'; + +const { Suite } = require('benchmark'); +const colors = require('ansi-colors'); +const argv = require('minimist')(process.argv.slice(2)); +const minimatch = require('minimatch'); +const compile = require('../lib/compile'); +const expand = require('../lib/expand'); +const parse = require('../lib/parse'); + +/** + * 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('parse set') + .add('picomatch', () => parse('foo/{a,b,c}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar')) + .run(); + +bench('parse nested sets') + .add('picomatch', () => parse('foo/{a,b,{x,y,z}}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar')) + .run(); + +bench('parse range') + .add('picomatch', () => parse('foo/{a..z}/bar')) + .add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar')) + .run(); + +bench.skip('expand') + .add('picomatch', () => expand(parse('foo/{a,b,c}/bar'))) + .add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar')) + .run(); + +bench.skip('compile') + .add('picomatch', () => compile(parse('foo/{a,b,c}/bar'))) + .add('minimatch', () => minimatch.makeRe('foo/{a,b,c}/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/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..e06f28c 100644 --- a/index.js +++ b/index.js @@ -1,318 +1,28 @@ 'use strict'; -/** - * Module dependencies - */ +const { MAX_LENGTH } = require('./lib/constants'); +const compile = require('./lib/compile'); +const expand = require('./lib/expand'); +const parse = require('./lib/parse'); -var toRegex = require('to-regex'); -var unique = require('array-unique'); -var extend = require('extend-shallow'); +const braces = (input, options = {}) => { -/** - * 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 = {}; -/** - * 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)). - * - * ```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'] - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {String} - * @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]; - } - - if (Array.isArray(pattern)) { - for (var i = 0; i < pattern.length; i++) { - arr.push.apply(arr, braces.create(pattern[i], options)); - } - } else { - arr = braces.create(pattern, options); - } - - if (options && options.nodupes === true) { - arr = unique(arr); - } - - if (!disabled) { - cache[key] = arr; - } - return arr; -} - -/** - * 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. - * - * ```js - * var braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/b/d', 'a/c/d']; - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ - -braces.expand = function(pattern, options) { - return braces.create(pattern, extend({}, options, {expand: true})); -}; - -/** - * Expands 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.expand('a/{b,c}/d')); - * //=> ['a/(b|c)/d'] - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ - -braces.optimize = function(pattern, options) { - return braces.create(pattern, 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'); - * 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 {String} `pattern` Brace pattern - * @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'); - } - - 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); }; -/** - * Create a regular expression from the given string `pattern`. - * - * ```js - * var braces = require('braces'); - * - * console.log(braces.makeRe('id-{200..300}')); - * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/ - * ``` - * @param {String} `pattern` The pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} - * @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') { + throw new TypeError('Expected a string'); } - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); + 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})`); } - function makeRe() { - var arr = braces(pattern, options); - var opts = extend({strictErrors: false}, options); - return toRegex(arr, opts); - } - - 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); -}; - -/** - * Compile the given `ast` or string with the given `options`. - * - * ```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: [] } - * ``` - * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first. - * @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(); - * ``` - * @api public - */ - -braces.clearCache = function() { - cache = braces.cache = {}; + return expand(parse(input, options), options); }; -/** - * 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. - */ - -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); - } - - if (cache.hasOwnProperty(key)) { - return cache[key]; - } - - 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; - -/** - * Expose `braces` - * @type {Function} - */ - 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..fe11bf1 --- /dev/null +++ b/lib/compile.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = (ast, options = {}) => { + let compile = node => { + let output = ''; + if (node.value) { + return node.value; + } + + if (node.nodes) { + for (let child of node.nodes) { + output += compile(child); + } + } + return output; + }; + + return compile(ast); +}; + 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..7cc9d4f --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,59 @@ +'use strict'; + +const path = require('path'); +const isWindows = process.platform === 'win32' || path.sep === '\\'; + +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_BACKWARD_SLASH: '\\', /* \ */ + CHAR_CARRIAGE_RETURN: '\r', /* \r */ + CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */ + CHAR_COLON: ':', /* : */ + CHAR_COMMA: ',', /* , */ + CHAR_DOT: '.', /* . */ + CHAR_DOUBLE_QUOTE: '"', /* " */ + CHAR_EQUAL: '=', /* = */ + CHAR_EXCLAMATION_MARK: '!', /* ! */ + CHAR_FORM_FEED: '\f', /* \f */ + CHAR_FORWARD_SLASH: '/', /* / */ + CHAR_GRAVE_ACCENT: '`', /* ` */ + 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..6bf66e0 --- /dev/null +++ b/lib/expand.js @@ -0,0 +1,126 @@ +'use strict'; + +const stringify = require('./stringify'); + +// let append = (stash, value) => { +// let len = stash.length; + +// if (stash[len - 1] === '') { +// stash[len - 1] = value; +// return; +// } + +// if (len) { +// for (let i = 0; i < len; i++) { +// stash[i] += value +// } +// } else { +// stash.push(value); +// } +// }; + +/** + * Flatten an array + */ + +const 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; +}; + +const append = (queue, stash) => { + let result = []; + + queue = [].concat(queue || []); + stash = [].concat(stash || []); + + if (!queue.length) return stash; + if (!stash.length) return queue; + + for (let item of queue) { + if (Array.isArray(item)) { + for (let i = 0; i < item.length; i++) { + item[i] = append(item[i], stash); + } + result.push(item); + } else { + for (let ele of stash) { + result.push(Array.isArray(ele) ? append(item, ele) : (item + ele)); + } + } + } + return result; +}; + +const expand = ast => { + console.log(ast) + console.log(ast.nodes.find(node => node.type === 'brace')); + + let walk = (node, parent = {}) => { + if (node.commas === 0 && node.ranges === 0) { + parent.queue.push(stringify(node)); + return; + } + + node.queue = []; + + for (let child of node.nodes) { + if (child.type === 'comma') { + node.queue.push(''); + continue; + } + + if (child.type === 'text') { + node.queue.push(append(node.queue.pop(), child.value)); + continue; + } + + if (child.type === 'close') { + parent.queue.push(append(parent.queue.pop(), node.queue)); + continue; + } + + if (child.nodes) { + walk(child, node); + } + } + + return node.queue; + }; + + return flatten(walk(ast)); +}; + +module.exports = expand; + +// const colors = require('ansi-colors'); +// const parse = require('./parse'); +// const cp = require('child_process'); +// const color = (arr, c) => arr.map(s => c(s)).join(', '); +// const braces = input => { +// return cp.execSync(`echo ${input}`).toString().trim().split(' '); +// }; + +// // 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}'; +// console.log(); +// console.log(' FIXTURE:', colors.magenta(fixture)); +// console.log(' ACTUAL:', color(compile(parse(fixture)), colors.yellow)); +// console.log('EXPECTED:', color(braces(fixture), colors.blue)); +// console.log(); + + diff --git a/lib/parse.js b/lib/parse.js new file mode 100644 index 0000000..608d966 --- /dev/null +++ b/lib/parse.js @@ -0,0 +1,300 @@ +'use strict'; + +// const compile = require('./compile'); +// const expand = require('./expand'); + +const escapeNode = (block, n = 0) => { + if (typeof block.nodes[n].value === 'string') { + block.nodes[n].value = '\\' + block.nodes[n].value; + } +}; + +const append = (block, node) => { + if (!block.queue) return; + + if (node.nodes) { + block.queue.push(node.queue); + return; + } + + let last = block.queue[block.queue.length - 1]; + + if ((node.type === 'comma' || node.type === 'range')) { + block.queue.push(node.value); + return; + } + + if (node.type === 'text' && node.value) { + if (typeof last !== 'string' || last === ',') { + block.queue.push(node.value); + } else { + block.queue[block.queue.length - 1] += node.value; + } + } +}; + +/** + * Constants + */ + +const { + CHAR_BACKWARD_SLASH, /* \ */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, /* ] */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE +} = require('./constants'); + +/** + * parse + */ + +const parse = (input, options = {}) => { + let ast = { type: 'root', nodes: [] }; + let stack = [ast]; + let length = input.length; + let block = ast; + let prev = ast; + let index = 0; + let depth = 0; + let value; + + /** + * Helpers + */ + + const advance = () => input[index++]; + const push = node => { + // append(block, node); + + if (prev && prev.type === 'text' && node.type === 'text') { + prev.value += node.value; + return; + } + + block.nodes.push(node); + // node.parent = block; + // node.prev = prev; + // prev.next = node; + + Reflect.defineProperty(node, 'parent', { value: block }); + Reflect.defineProperty(node, 'prev', { value: prev }); + Reflect.defineProperty(prev, 'next', { value: node }); + 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_BACKWARD_SLASH) { + value += advance(); + push({ type: 'text', 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_BACKWARD_SLASH) { + value += advance(); + continue; + } + + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + closed = true; + break; + } + } + + if (closed !== true) { + value = '\\' + value; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Right square bracket (literal): ']' + */ + + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: 'text', value: '\\' + value }); + continue; + } + + /** + * Left curly brace: '{' + */ + + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + block = push({ type: 'brace', commas: 0, ranges: 0, nodes: [] }); + stack.push(block); + push({ type: 'open', value }); + continue; + } + + /** + * Right curly brace: '}' + */ + + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (index === 1 || block.type !== 'brace') { + push({ type: 'text', value: '\\' + value }); + continue; + } + + let type = 'close'; + block = stack.pop(); + + // detect invalid braces + if ((block.commas === 0 && block.ranges === 0) || (block.commas > 0 && block.ranges > 0) || block.ranges > 2) { + + type = 'text'; + block.literal = true; + block.commas = 0; + block.ranges = 0; + + // escape open/close braces if specified on options + if (options.escapeInvalid === true) { + escapeNode(block); + value = '\\' + value; + } + } + + push({ type, value }); + depth--; + + block = stack[stack.length - 1]; + continue; + } + + /** + * Comma: ',' + */ + + if (value === CHAR_COMMA && depth > 0) { + push({ type: 'comma', value }); + block.commas++; + continue; + } + + /** + * Dot: '.' + */ + + if (value === CHAR_DOT && depth > 0) { + let siblings = block.nodes; + + if (depth === 0 || siblings.length === 0) { + push({ type: 'text', value }); + continue; + } + + if (prev.type === 'dot') { + prev.value += value; + prev.type = 'range'; + + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + 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 }); + } + + // Fix imbalanced braces and brackets + do { + if (block.literal !== true && (block.type === 'brace' || block.type === 'bracket')) { + block.literal = true; + block.commas = 0; + block.ranges = 0; + escapeNode(block); + } + block = stack.pop(); + } while (stack.length > 0); + + push({ type: 'eos' }); + return ast; +}; + +// const braces = require('../tmp/index'); +// 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'; +// console.log(braces.expand(input)); +// 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]')) + +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..fe11bf1 --- /dev/null +++ b/lib/stringify.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = (ast, options = {}) => { + let compile = node => { + let output = ''; + if (node.value) { + return node.value; + } + + if (node.nodes) { + for (let child of node.nodes) { + output += compile(child); + } + } + return output; + }; + + return compile(ast); +}; + diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 4716671..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,343 +0,0 @@ -'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; - } - 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. - */ - -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; -}; - -/** - * Normalize options - */ - -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; -}; - -/** - * Join patterns in `a` to patterns in `b` - */ - -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]; - - if (Array.isArray(bval)) { - arr.push(utils.join(val, bval, options)); - } else { - arr.push(val + bval); - } - } - } - return arr; -}; - -/** - * Split the given string on `,` if not escaped. - */ - -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; - } - return splitString(str, opts, utils.escapeBrackets(opts)); -}; - -/** - * Expand ranges or sets in the given `pattern`. - * - * @param {String} `str` - * @param {Object} `options` - * @return {Object} - */ - -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; - } - - 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; - } - 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('|')]; -}; - -/** - * Cast `val` to an array. - * @param {*} `val` - */ - -utils.arrayify = function(arr) { - if (typeof arr === 'undefined') { - return []; - } - if (typeof arr === 'string') { - return [arr]; - } - return arr; -}; - -/** - * Returns true if the given `str` is a non-empty string - * @return {Boolean} - */ - -utils.isString = function(str) { - return str != null && typeof str === 'string'; -}; - -/** - * Get the last element from `array` - * @param {Array} `array` - * @return {*} - */ - -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; - -utils.escapeRegex = function(str) { - return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1'); -}; diff --git a/package.json b/package.json index 96cbb2b..3f983fd 100644 --- a/package.json +++ b/package.json @@ -29,35 +29,16 @@ "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" + "extend-shallow": "^3.0.2", + "to-regex": "^3.0.2" }, "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", + "ansi-colors": "^3.2.4", + "brace-expansion": "^1.1.11", + "gulp-format-md": "^2.0.0", "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" + "mocha": "^6.0.2" }, "keywords": [ "alpha", diff --git a/test/bash.expanded.js b/test/bash-expanded-braces.js similarity index 96% rename from test/bash.expanded.js rename to test/bash-expanded-braces.js index f2a4951..68e1c77 100644 --- a/test/bash.expanded.js +++ b/test/bash-expanded-braces.js @@ -1,21 +1,31 @@ 'use strict'; -var extend = require('extend-shallow'); -var assert = require('assert'); -var braces = require('..'); +require('mocha'); +const assert = require('assert'); +const braces = require('..'); -function equal(pattern, expected, options) { - var actual = braces.expand(pattern, options); - assert.deepEqual(actual.sort(), expected.sort(), pattern); -} +const equal = (input, expected, options) => { + assert.deepStrictEqual(braces.expand(input, options), expected, input); +}; /** * 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() { +describe('bash.expanded', () => { + it.only('should expand', () => { + // console.log(expand('{a,{b,c},d}')); + // console.log(expand('{{a,b},{c,d},{e,f}}')); + // console.log(expand('{a,b}{c,d}{e,f}')); + // console.log(expand('a/{b,c{x,y}d,e}/f')); + // console.log(expand('{{a,b}/{c,d{e,f}g,h}/i,j,k}')); + // console.log(braces.expand('{{a,b}/i,j,k}')); + console.log(braces.expand('{1..5..2..}')); + console.log(braces.expand('{1..5..2}')); + }); + + it.skip('should throw an error when range exceeds rangeLimit', () => { + assert.throws(() => { braces.expand('{214748364..2147483649}'); }); }); @@ -397,21 +407,17 @@ describe('bash.expanded', function() { [ '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) { + 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; + if (options.skip !== true) { + it('should compile: ' + pattern, () => equal(pattern, expected, options)); } - - 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/bash.spec.js b/test/bash.spec.js deleted file mode 100644 index bf22ab4..0000000 --- a/test/bash.spec.js +++ /dev/null @@ -1,189 +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).sort(); - assert.deepEqual(actual, expected.sort(), pattern); -} - -/** - * Bash 4.3 unit tests - */ - -describe('bash', function() { - var fixtures = [ - [ '{1\\.2}', {}, [ '{1.2}' ] ], - [ '{1\\.2}', {unescape: false}, [ '{1\\.2}' ] ], - [ '{"x,x"}', {}, [ '{x,x}' ] ], - [ '{x","x}', {}, [ '{x,x}' ] ], - [ '\'{x,x}\'', {}, [ '{x,x}' ] ], - [ '{x`,`x}', {}, [ '{x,x}' ] ], - [ '{x`,`x}', {unescape: false}, [ '{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' ] ], - [ 'a{b}c', {}, [ 'a{b}c' ] ], - [ 'a{1..5}b', {}, [ 'a1b', 'a2b', 'a3b', 'a4b', 'a5b' ] ], - [ 'a{01..5}b', {}, [ 'a01b', 'a02b', 'a03b', 'a04b', 'a05b' ] ], - [ 'a{-01..5}b', {}, [ 'a-01b', 'a000b', 'a001b', 'a002b', 'a003b', 'a004b', 'a005b' ] ], - [ 'a{-01..5..3}b', {}, [ 'a-01b', 'a002b', 'a005b' ] ], - [ 'a{001..9}b', {}, [ 'a001b', 'a002b', 'a003b', 'a004b', 'a005b', 'a006b', 'a007b', 'a008b', 'a009b' ] ], - [ 'a{b,c{d,e},{f,g}h}x{y,z', {}, [ 'abx{y,z', 'acdx{y,z', 'acex{y,z', 'afhx{y,z', 'aghx{y,z' ] ], - [ 'a{b,c{d,e},{f,g}h}x{y,z\\}', {}, [ 'abx{y,z}', 'acdx{y,z}', 'acex{y,z}', 'afhx{y,z}', 'aghx{y,z}' ] ], - [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ], - [ 'a{b{c{d,e}f{x,y{{g}h', {}, [ 'a{b{cdf{x,y{{g}h', 'a{b{cef{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' ] ], - [ '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}g}h', {}, [ 'a{b{cdf}g}h', 'a{b{cef}g}h' ] ], - [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ], - [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ], - [ 'f{x,y{{g,z}}h', {}, [ 'f{x,y{g}h', 'f{x,y{z}h' ] ], - [ 'f{x,y{{g,z}}h}', {}, [ 'fx', 'fy{g}h', 'fy{z}h' ] ], - [ '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', {}, [ 'fxh', 'fy{}gh' ] ], - [ 'z{a,b{,c}d', {}, [ 'z{a,bd', 'z{a,bcd' ] ], - [ 'z{a,b},c}d', {}, [ 'za,c}d', 'zb,c}d' ] ], - [ '{-01..5}', {}, [ '-01', '000', '001', '002', '003', '004', '005' ] ], - [ '{-05..100..5}', {}, [ '-05', '000', '005', '010', '015', '020', '025', '030', '035', '040', '045', '050', '055', '060', '065', '070', '075', '080', '085', '090', '095', '100' ] ], - [ '{-05..100}', {}, [ '-05', '-04', '-03', '-02', '-01', '000', '001', '002', '003', '004', '005', '006', '007', '008', '009', '010', '011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061', '062', '063', '064', '065', '066', '067', '068', '069', '070', '071', '072', '073', '074', '075', '076', '077', '078', '079', '080', '081', '082', '083', '084', '085', '086', '087', '088', '089', '090', '091', '092', '093', '094', '095', '096', '097', '098', '099', '100' ] ], - [ '{0..5..2}', {}, [ '0', '2', '4' ] ], - [ '{0001..05..2}', {}, [ '0001', '0003', '0005' ] ], - [ '{0001..-5..2}', {}, [ '0001', '-001', '-003', '-005' ] ], - [ '{0001..-5..-2}', {}, [ '0001', '-001', '-003', '-005' ] ], - [ '{0001..5..-2}', {}, [ '0001', '0003', '0005' ] ], - [ '{01..5}', {}, [ '01', '02', '03', '04', '05' ] ], - [ '{1..05}', {}, [ '01', '02', '03', '04', '05' ] ], - [ '{1..05..3}', {}, [ '01', '04' ] ], - [ '{05..100}', {}, [ '005', '006', '007', '008', '009', '010', '011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061', '062', '063', '064', '065', '066', '067', '068', '069', '070', '071', '072', '073', '074', '075', '076', '077', '078', '079', '080', '081', '082', '083', '084', '085', '086', '087', '088', '089', '090', '091', '092', '093', '094', '095', '096', '097', '098', '099', '100' ] ], - [ '{0a..0z}', {}, [ '{0a..0z}' ] ], - [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ], - [ '{a,b{c,d}', {}, [ '{a,bc', '{a,bd' ] ], - [ '{a,b}c,d}', {}, [ 'ac,d}', 'bc,d}' ] ], - [ '{a..F}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F' ] ], - [ '{A..f}', {}, [ '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..Z}', {}, [ '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' ] ], - [ '{z..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', '`', '_', '^', ']', '\\', '[', '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' ] ], - [ '{Z..a}', {}, [ 'Z', '[', '\\', ']', '^', '_', '`', 'a' ] ], - [ '{a..F..2}', {}, [ 'a', '_', ']', '[', 'Y', 'W', 'U', 'S', 'Q', 'O', 'M', 'K', 'I', 'G' ] ], - [ '{A..f..02}', {}, [ 'A', 'C', 'E', 'G', 'I', 'K', 'M', 'O', 'Q', 'S', 'U', 'W', 'Y', '[', ']', '_', 'a', 'c', 'e' ] ], - [ '{a..Z..5}', {}, [ 'a', '\\' ] ], - [ 'd{a..Z..5}b', {}, [ 'dab', 'd\\b' ] ], - [ '{A..z..10}', {}, [ 'A', 'K', 'U', '_', 'i', 's' ] ], - [ '{z..A..-2}', {}, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b', '`', '^', '\\', 'Z', 'X', 'V', 'T', 'R', 'P', 'N', 'L', 'J', 'H', 'F', 'D', 'B' ] ], - [ '{Z..a..20}', {}, [ 'Z' ] ], - [ '{a{,b}', {}, [ '{a', '{ab' ] ], - [ '{a\\},b}', {}, [ 'a}', 'b' ] ], - [ '{x,y{,}g}', {}, [ 'x', 'yg', 'yg' ] ], - [ '{x,y{}g}', {}, [ 'x', 'y{}g' ] ], - [ '{{a,b}', {}, [ '{a', '{b' ] ], - [ '{{a,b},c}', {}, [ 'a', 'b', 'c' ] ], - [ '{{a,b}c}', {}, [ '{ac}', '{bc}' ] ], - [ '{{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}', {}, [ 'ac}', '{ac}', 'bc}' ] ], - [ 'a,\\{b,c}', {}, [ 'a,{b,c}' ] ], - [ '{-10.\\.00}', {}, [ '{-10..00}' ] ], - [ 'ff{c,b,a}', {}, [ 'ffc', 'ffb', 'ffa' ] ], - [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ], - [ '{l,n,m}xyz', {}, [ 'lxyz', 'nxyz', 'mxyz' ] ], - [ '{abc\\,def}', {}, [ '{abc,def}' ] ], - [ '{abc}', {}, [ '{abc}' ] ], - [ '{x\\,y,\\{abc\\},trie}', {}, [ 'x,y', '{abc}', 'trie' ] ], - [ '{}', {}, [ '{}' ] ], - [ '{ }', {}, [ '{ }' ] ], - [ '}', {}, [ '}' ] ], - [ '{', {}, [ '{' ] ], - [ 'abcd{efgh', {}, [ 'abcd{efgh' ] ], - [ 'foo {1,2} bar', {}, [ 'foo 1 bar', 'foo 2 bar' ] ], - [ '"${var}"{x,y}', {}, [ '${var}x', '${var}y' ] ], - [ '{1..10}', {}, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ], - [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ], - [ '{{0..10},braces}', {}, [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces' ] ], - [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y', 'xbracesy' ] ], - [ '{3..3}', {}, [ '3' ] ], - [ 'x{3..3}y', {}, [ 'x3y' ] ], - [ '{10..1}', {}, [ '10', '9', '8', '7', '6', '5', '4', '3', '2', '1' ] ], - [ '{10..1}y', {}, [ '10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y' ] ], - [ 'x{10..1}y', {}, [ 'x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y' ] ], - [ '{a..f}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ], - [ '{f..a}', {}, [ 'f', 'e', 'd', 'c', 'b', 'a' ] ], - [ '{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' ] ], - [ '{f..f}', {}, [ 'f' ] ], - [ '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' ] ], - [ '{-1..-10}', {}, [ '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10' ] ], - [ '{-20..0}', {}, [ '-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0' ] ], - [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ], - [ 'a-{bdef-{g,i}-c', {}, [ 'a-{bdef-g-c', 'a-{bdef-i-c' ] ], - [ '{"klklkl"}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ], - [ '{"x,x"}', {}, [ '{x,x}' ] ], - [ '{klklkl}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ], - [ '{1..10..2}', {}, [ '1', '3', '5', '7', '9' ] ], - [ '{-1..-10..2}', {}, [ '-1', '-3', '-5', '-7', '-9' ] ], - [ '{-1..-10..-2}', {}, [ '-1', '-3', '-5', '-7', '-9' ] ], - [ '{10..1..-2}', {}, [ '10', '8', '6', '4', '2' ] ], - [ '{10..1..2}', {}, [ '10', '8', '6', '4', '2' ] ], - [ '{1..20..2}', {}, [ '1', '3', '5', '7', '9', '11', '13', '15', '17', '19' ] ], - [ '{1..20..20}', {}, [ '1' ] ], - [ '{100..0..5}', {}, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ], - [ '{100..0..-5}', {}, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ], - [ '{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..z..2}', {}, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ], - [ '{z..a..-2}', {}, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ], - [ '{2147483645..2147483649}', {}, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ], - [ '{10..0..2}', {}, [ '10', '8', '6', '4', '2', '0' ] ], - [ '{10..0..-2}', {}, [ '10', '8', '6', '4', '2', '0' ] ], - [ '{-50..-0..5}', {}, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ], - [ '{1..10.f}', {}, [ '{1..10.f}' ] ], - [ '{1..ff}', {}, [ '{1..ff}' ] ], - [ '{1..10..ff}', {}, [ '{1..10..ff}' ] ], - [ '{1.20..2}', {}, [ '{1.20..2}' ] ], - [ '{1..20..f2}', {}, [ '{1..20..f2}' ] ], - [ '{1..20..2f}', {}, [ '{1..20..2f}' ] ], - [ '{1..2f..2}', {}, [ '{1..2f..2}' ] ], - [ '{1..ff..2}', {}, [ '{1..ff..2}' ] ], - [ '{1..ff}', {}, [ '{1..ff}' ] ], - [ '{1..0f}', {}, [ '{1..0f}' ] ], - [ '{1..10f}', {}, [ '{1..10f}' ] ], - [ '{1..10.f}', {}, [ '{1..10.f}' ] ], - [ '{},b}.h', {}, [ '{},b}.h' ] ], - [ 'y{\\},a}x', {}, [ 'y}x', 'yax' ] ], - [ '{}a,b}c', {}, [ '{}a,b}c' ] ] - ]; - - 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..35e71cb 100644 --- a/test/braces.compile.js +++ b/test/braces.compile.js @@ -1,19 +1,55 @@ '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('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,,,}'); + }); }); - 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 support empty sets', () => { + assert.equal(compile(parse('{a,,,}')), '{a,,,}'); + }); + + 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 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 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}'); + assert.equal(compile(parse('{a..e,z}', { escapeInvalid: true })), '\\{a..e,z\\}'); + assert.equal(compile(parse('{a..e,a..z}', { escapeInvalid: true })), '\\{a..e,a..z\\}'); + }); + + 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..0e2d0bb --- /dev/null +++ b/test/braces.expand.js @@ -0,0 +1,13 @@ +'use strict'; + +require('mocha'); +const assert = require('assert').strict; +const expand = require('../lib/expand'); +const parse = require('../lib/parse'); + +describe('braces.expand()', () => { + it('should expand an AST', () => { + let actual = expand(parse('a/{b,c}/d')); + assert.deepEqual(actual, ['a/b/d', 'a/c/d']); + }); +}); 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..a81d694 100644 --- a/test/braces.parse.js +++ b/test/braces.parse.js @@ -1,18 +1,42 @@ '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('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}]'); + }); }); - it('should have an array of nodes', function() { - var ast = braces.parse('a/{b,c}/d'); - assert(Array.isArray(ast.nodes)); + 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 deleted file mode 100644 index ba935c8..0000000 --- a/test/minimatch.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var braces = require('..'); - -/** - * minimatch v3.0.3 unit tests - */ - -describe('brace expansion', function() { - var 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']], - ['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() { - assert.deepEqual(braces.expand(unit[0]), unit[1], unit[0]); - }); - }); -}); diff --git a/test/multiples.js b/test/multiples.js deleted file mode 100644 index ff8b244..0000000 --- a/test/multiples.js +++ /dev/null @@ -1,61 +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('multiples', function() { - var 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/{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/{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']], - ['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', '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', 'a', 'a', 'a', 'a', 'a', 'a', 'a']], - ['a{,,,,}', ['a', 'a', 'a', 'a', 'a']], - ['a{,,,}', ['a', 'a', 'a', 'a']], - ['a{,,}', ['a', 'a', 'a']], - ['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{,}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]); - }); - }); -}); 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-1.8.5.js deleted file mode 100644 index 00f652a..0000000 --- a/test/regression-1.8.5.js +++ /dev/null @@ -1,463 +0,0 @@ -'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() { - 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']); - equal('{abc\\,d,ef}', ['abc,d', 'ef']); - equal('{abc}', ['{abc}']); - - equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']); - 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('{}', ['{}']); - equal('{ }', ['{ }']); - equal('}', ['}']); - equal('{', ['{']); - equal('abcd{efgh', ['abcd{efgh']); - - equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']); - }); - - it('new sequence brace operators', function() { - 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']); - }); - - it('ranges', function() { - equal('{3..3}', ['3']); - equal('x{3..3}y', ['x3y']); - 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('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']); - equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']); - equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']); - - 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('{f..f}', ['f']); - 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() { - // 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']); - }); - - it('do negative numbers work?', function() { - 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() { - 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']); - - equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']); - - equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']); - equal('{"x,x"}', ['{x,x}']); - }); - - it('numerical ranges with steps', function() { - 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']); - - equal('{10..1..-2}', ['10', '8', '6', '4', '2']); - equal('{10..1..2}', ['10', '8', '6', '4', '2']); - - equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); - equal('{1..20..20}', ['1']); - - 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('{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() { - 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() { - equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); - }); - - 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']); - }); - - it('bad', function() { - equal('{1..10.f}', ['{1..10.f}']); - equal('{1..ff}', ['{1..ff}']); - equal('{1..10..ff}', ['{1..10..ff}']); - equal('{1.20..2}', ['{1.20..2}']); - equal('{1..20..f2}', ['{1..20..f2}']); - equal('{1..20..2f}', ['{1..20..2f}']); - equal('{1..2f..2}', ['{1..2f..2}']); - equal('{1..ff..2}', ['{1..ff..2}']); - equal('{1..ff}', ['{1..ff}']); - equal('{1..0f}', ['{1..0f}']); - equal('{1..10f}', ['{1..10f}']); - equal('{1..10.f}', ['{1..10.f}']); - equal('{1..10.f}', ['{1..10.f}']); - }); -}); - -describe('bash tests', function() { - describe('brace expansion', function() { - it('should return an empty array when no braces are found', function() { - equal('', ['']); - }); - - it('should expand emty sets', function() { - equal('a{,}', ['a', 'a']); - equal('{,}b', ['b', 'b']); - equal('a{,}b', ['ab', 'ab']); - equal('a{,}', ['a', 'a']); - 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,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 work with no braces', function() { - equal('abc', ['abc']); - }); - - it('should work with no commas', function() { - equal('a{b}c', ['a{b}c']); - }); - - it('should work with no commas in `bash` mode', function() { - equal('a{b}c', ['a{b}c']); - }); - - it('should handle spaces', function() { - 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 - 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() { - equal('{ }', ['{ }']); - equal('{}', ['{}']); - equal('}', ['}']); - equal('{', ['{']); - equal('{,}', []); - }); - - it('should handle 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{cefxh', 'a{b{cdfy{}gh', '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() { - 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']); - }); - - it('should return invalid braces:', function() { - equal('{0..10,braces}', ['0..10', 'braces']); - }); - - it('should not expand quoted strings.', function() { - equal('{"x,x"}', ['{x,x}']); - equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']); - }); - - it('should work with one value', function() { - 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() { - 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() { - 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() { - 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() { - 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() { - equal('a{b,}c', ['abc', 'ac']); - equal('a{,b}c', ['ac', 'abc']); - }); - - it('should expand sets', function() { - 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']); - equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']); - equal('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']); - 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 nested sets', function() { - 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,c{d,e}f}g', ['abg', 'acdfg', 'acefg']); - equal('a{{x,y},z}b', ['axb', 'azb', 'ayb']); - 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{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/e/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.txt']); - }); - - it('should expand with extglobs.', function() { - 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() { - 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', ['a{b,c}d']); - equal('a{b\\,c\\,d}e', ['a{b,c,d}e']); - 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 in `bash` mode.', function() { - 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() { - 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,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() { - 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() { - 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('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']); - 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('{3..3}', ['3']); - equal('{5..8}', ['5', '6', '7', '8']); - }); - - it('should expand alphabetical ranges', function() { - 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']); - 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..a}', ['f', 'e', 'd', 'c', 'b', 'a']); - equal('{f..f}', ['f']); - }); - - it('should use steps with alphabetical ranges', function() { - equal('{a..e..2}', ['a', 'c', 'e']); - equal('{E..A..2}', ['E', 'C', 'A']); - }); - - 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}'], {optimize: true}); - equal('{1.1..~2.1}', ['{1.1..~2.1}'], {optimize: true}); - }); - - it('should expand negative ranges', function() { - 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 work with dots in file paths', function() { - 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() { - 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']); - 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..0..2}', ['10', '8', '6', '4', '2', '0']); - equal('{10..1..-2}', ['10', '8', '6', '4', '2']); - equal('{10..1..2}', ['10', '8', '6', '4', '2']); - 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('{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('{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('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']); - 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..1..-2}', ['10', '8', '6', '4', '2']); - equal('{10..1..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']); - }); - - 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('{-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']); - equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']); - }); - - it('should return invalid ranges:', function() { - equal('{1.20..2}', ['{1.20..2}']); - 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}']); - }); -}); 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']); - }); - }); -});