Skip to content

Commit

Permalink
braces api
Browse files Browse the repository at this point in the history
  • Loading branch information
jonschlinkert committed Apr 1, 2019
1 parent 80eae1f commit 7b106b6
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 49 deletions.
37 changes: 15 additions & 22 deletions bench/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ 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');
const braces = require('..');

/**
* Setup
Expand Down Expand Up @@ -52,27 +50,22 @@ bench.skip = name => {
return skip;
};

bench('parse set')
.add(' braces', () => parse('foo/{a,b,c}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar'))
.run();
// bench('expand - set')
// .add(' braces', () => braces.expand('foo/{a,b,c}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar'))
// .run();

bench('parse nested sets')
.add(' braces', () => parse('foo/{a,b,{x,y,z}}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar'))
.run();
// bench('expand - nested sets')
// .add(' braces', () => braces.expand('foo/{a,b,{x,y,z}}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar'))
// .run();

bench('parse range')
.add(' braces', () => parse('foo/{a..z}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar'))
.run();

bench.skip('expand')
.add(' braces', () => expand(parse('foo/{a,b,c}/bar')))
.add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar'))
.run();
// bench('expand - range')
// .add(' braces', () => braces.expand('foo/{a..z}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar'))
// .run();

bench.skip('compile')
.add(' braces', () => compile(parse('foo/{a,b,c}/bar')))
bench('compile regex - set')
.add(' braces', () => braces.makeRe(parse('foo/{a,b,c}/bar')))
.add('minimatch', () => minimatch.makeRe('foo/{a,b,c}/bar'))
.run();
169 changes: 167 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,59 @@
'use strict';

const { MAX_LENGTH } = require('./lib/constants');
const stringify = require('./lib/stringify');
const compile = require('./lib/compile');
const expand = require('./lib/expand');
const parse = require('./lib/parse');
const toRegex = require('to-regex');

/**
* Expand the given pattern or create a regex-compatible string.
*
* ```js
* const braces = require('braces');
* console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
* console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {String}
* @api public
*/

const braces = (input, options = {}) => {
let result = [];

if (Array.isArray(input)) {
for (let i = 0; i < input.length; i++) {
result.push(...braces.create(input[i], options));
}
} else {
result = braces.create(input, options);
}

if (options && options.nodupes === true) {
result = [...new Set(result)];
}

return result;
};

braces.expand = (input, options = {}) => {
/**
* Parse the given `str` with the given `options`.
*
* ```js
* // braces.parse(pattern, [, options]);
* const ast = braces.parse('a/{b,c}/d');
* console.log(ast);
* ```
* @param {String} pattern Brace pattern to parse
* @param {Object} options
* @return {Object} Returns an AST
* @api public
*/

braces.parse = (input, options = {}) => {
if (typeof input !== 'string') {
throw new TypeError('Expected a string');
}
Expand All @@ -22,7 +64,130 @@ braces.expand = (input, options = {}) => {
throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
}

return expand(parse(input, options), options);
return parse(input, options);
};

/**
* Creates a braces string from an AST, or an AST node.
*
* ```js
* const braces = require('braces');
* let ast = braces.parse('foo/{a,b}/bar');
* console.log(stringify(ast.nodes[2])); //=> '{a,b}'
* ```
* @param {String} `input` Brace pattern or AST.
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/

braces.stringify = (input, options = {}) => {
if (typeof input === 'string') {
return stringify(braces.parse(input, options), options);
}
return stringify(input, options);
};

/**
* Compiles a brace pattern into a regex-compatible, optimized string.
* This method is called by the main [braces](#braces) function by default.
*
* ```js
* const braces = require('braces');
* console.log(braces.compile('a/{b,c}/d'));
* //=> ['a/(b|c)/d']
* ```
* @param {String} `input` Brace pattern or AST.
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/

braces.compile = (input, options = {}) => {
if (typeof input === 'string') {
return compile(braces.parse(input, options), options);
}
return compile(input, options);
};

/**
* Expands a brace pattern into an array. This method is called by the
* main [braces](#braces) function when `options.expand` is true. Before
* using this method it's recommended that you read the [performance notes](#performance))
* and advantages of using [.compile](#compile) instead.
*
* ```js
* const 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 = (input, options = {}) => {
if (typeof input === 'string') {
return expand(braces.parse(input, options), options);
}
return expand(input, options);
};

/**
* Processes a brace pattern and returns either an expanded array
* (if `options.expand` is true), a highly optimized regex-compatible string.
* This method is called by the main [braces](#braces) function.
*
* ```js
* const braces = require('braces');
* console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
* //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/

braces.create = (input, options = {}) => {
if (input === '' || input.length < 3) {
return [input];
}

let result = options.expand !== true
? braces.compile(input, options)
: braces.expand(input, options);

// filter out empty strings if specified
if (options.noempty === true) {
result = result.filter(Boolean);
}

// filter out duplicates if specified
if (options.nodupes === true) {
result = [...new Set(result)];
}

return result;
};

/**
* Create a regular expression from the given string `pattern`.
*
* ```js
* const braces = require('braces');
* console.log(braces.makeRe('foo/id-{200..300}'));
* //=> /^(?:foo/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 = (pattern, options) => {
return toRegex(braces.compile(pattern, options), { strictErrors: false, ...options });
};

module.exports = braces;
9 changes: 5 additions & 4 deletions lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

const utils = require('./utils');

module.exports = (ast, options = {}) => {
let compile = (node, parent = {}) => {
const compile = (ast, options = {}) => {
let walk = (node, parent = {}) => {
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
let invalidNode = node.invalid === true && options.escapeInvalid === true;
let output = '';
Expand All @@ -17,12 +17,13 @@ module.exports = (ast, options = {}) => {

if (node.nodes) {
for (let child of node.nodes) {
output += compile(child, node);
output += walk(child, node);
}
}
return output;
};

return compile(ast);
return walk(ast);
};

module.exports = compile;
7 changes: 2 additions & 5 deletions lib/expand.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

const stringify = require('./stringify');
const compile = require('./compile');
const utils = require('./utils');

Expand All @@ -27,13 +26,11 @@ const append = (queue = '', stash = '', enclose = false) => {
}
}
}
return result;
return utils.flatten(result);
};

const expand = (ast, options = {}) => {
let walk = (node, parent = {}) => {
let enclose = utils.encloseBrace(node);
// let invalid = options.escapeInvalid && utils.isInvalidBrace(parent);
node.queue = [];

if (node.invalid || node.dollar) {
Expand All @@ -46,12 +43,12 @@ const expand = (ast, options = {}) => {
return;
}

let enclose = utils.encloseBrace(node);
for (let i = 0; i < node.nodes.length; i++) {
let child = node.nodes[i];

if (child.type === 'comma') {
node.queue.push('');

if (i === 1) {
node.queue.push('');
}
Expand Down
2 changes: 0 additions & 2 deletions lib/parse.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

// const compile = require('./compile');
// const expand = require('./expand');
const utils = require('./utils');

/**
Expand Down
16 changes: 10 additions & 6 deletions lib/stringify.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
'use strict';

module.exports = (ast, options = {}) => {
let compile = node => {
let invalid = parent.invalid === true && options.escapeInvalid === true;
let stringify = (node, parent = {}) => {
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
let invalidNode = node.invalid === true && options.escapeInvalid === true;
let output = '';

if (invalid && !node.escaped && (node.type === 'open' || node.type === 'close')) {
return '\\' + node.value;
if (node.value) {
if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
return '\\' + node.value;
}
return node.value;
}

if (node.value) {
Expand All @@ -15,12 +19,12 @@ module.exports = (ast, options = {}) => {

if (node.nodes) {
for (let child of node.nodes) {
output += compile(child);
output += stringify(child);
}
}
return output;
};

return compile(ast);
return stringify(ast);
};

4 changes: 0 additions & 4 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ exports.isOpenOrClose = node => {
*/

exports.flatten = (...args) => {
if (typeof Array.prototype.flat === 'function') {
return args.flat(Infinity);
}

const result = [];
const flat = arr => {
for (let i = 0; i < arr.length; i++) {
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,16 @@
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
"node": ">=8"
},
"scripts": {
"test": "mocha",
"benchmark": "node benchmark"
},
"devDependencies": {
"ansi-colors": "^3.2.4",
"bash-path": "^2.0.1",
"brace-expansion": "^1.1.11",
"cross-spawn": "^6.0.5",
"gulp-format-md": "^2.0.0",
"minimatch": "^3.0.4",
"mocha": "^6.0.2"
},
"keywords": [
Expand Down Expand Up @@ -81,5 +79,8 @@
"nanomatch"
]
}
},
"dependencies": {
"to-regex": "^3.0.2"
}
}

0 comments on commit 7b106b6

Please sign in to comment.