Bash-like brace expansion library for JavaScript with complete Bash 4.3 specification support
npx @tessl/cli install tessl/npm-braces@3.0.0Braces is a JavaScript library that provides Bash-like brace expansion functionality. It can expand brace patterns into arrays of strings or compile them into optimized regex-compatible strings for pattern matching. The library supports the complete Bash 4.3 braces specification with enhanced safety features to prevent malicious regex attacks.
npm install bracesconst braces = require('braces');ESM import:
import braces from 'braces';const braces = require('braces');
// Compile to regex-compatible strings (default behavior)
console.log(braces('{a,b,c}'));
//=> ['(a|b|c)']
console.log(braces('a/{x,y,z}/b'));
//=> ['a/(x|y|z)/b']
// Expand to full arrays
console.log(braces('{a,b,c}', { expand: true }));
//=> ['a', 'b', 'c']
console.log(braces('a/{x,y,z}/b', { expand: true }));
//=> ['a/x/b', 'a/y/b', 'a/z/b']
// Sequences and ranges
console.log(braces('{1..5}', { expand: true }));
//=> ['1', '2', '3', '4', '5']
console.log(braces('{01..05}', { expand: true }));
//=> ['01', '02', '03', '04', '05']Braces is built around several key components that work together to provide flexible brace expansion:
The library uses a two-phase approach: parsing creates a structured representation, then compilation or expansion produces the final output based on the requested mode.
The primary interface for brace expansion and compilation.
/**
* Expand the given pattern or create a regex-compatible string
* @param {string|string[]} input - Brace pattern(s) to process
* @param {BraceOptions} options - Configuration options
* @returns {string[]} Array of expanded or compiled strings
*/
function braces(input, options = {});Parse brace patterns into an Abstract Syntax Tree for advanced manipulation.
/**
* Parse brace pattern into AST
* @param {string} input - Brace pattern to parse
* @param {BraceOptions} options - Configuration options
* @returns {BraceAST} Abstract syntax tree representation
*/
braces.parse(input, options = {});Convert an AST back into a brace pattern string.
/**
* Creates a braces string from an AST or AST node
* @param {string|BraceAST} input - Brace pattern or AST
* @param {BraceOptions} options - Configuration options
* @returns {string} Reconstructed brace pattern string
*/
braces.stringify(input, options = {});Compile brace patterns into regex-compatible, optimized strings.
/**
* Compiles a brace pattern into a regex-compatible, optimized string
* @param {string|BraceAST} input - Brace pattern or AST
* @param {BraceOptions} options - Configuration options
* @returns {string[]} Array of regex-compatible strings
*/
braces.compile(input, options = {});Expand brace patterns into arrays of all possible string combinations.
/**
* Expands a brace pattern into an array of strings
* @param {string|BraceAST} input - Brace pattern or AST
* @param {BraceOptions} options - Configuration options
* @returns {string[]} Array of expanded strings
*/
braces.expand(input, options = {});Lower-level function that processes patterns based on options.
/**
* Processes a brace pattern and returns either expanded array or optimized regex string
* @param {string} input - Brace pattern
* @param {BraceOptions} options - Configuration options
* @returns {string[]} Array of processed strings
*/
braces.create(input, options = {});/**
* Configuration options for brace processing
*/
interface BraceOptions {
/** Generate expanded array instead of compiled regex (default: undefined) */
expand?: boolean;
/** Maximum input string length (default: 10000) */
maxLength?: number;
/** Remove duplicates from results (default: undefined) */
nodupes?: boolean;
/** Maximum range expansion size (default: 1000) */
rangeLimit?: number;
/** Filter out empty strings from results (default: undefined) */
noempty?: boolean;
/** Custom transformation function for range values. For non-numeric ranges, 'value' is a character code. For numeric ranges, 'value' is the number. */
transform?: (value: any, index: number) => any;
/** Treat patterns as regex quantifiers instead of lists (default: undefined) */
quantifiers?: boolean;
/** Preserve escape characters in output (default: undefined) */
keepEscaping?: boolean;
/** Preserve quotes in parsed output (default: undefined) */
keepQuotes?: boolean;
/** Escape invalid brace patterns (default: undefined) */
escapeInvalid?: boolean;
/** Step/increment for ranges */
step?: number;
/** Wrap ranges in parentheses (passed to fill-range) */
wrap?: boolean;
/** Convert ranges to regex (passed to fill-range) */
toRegex?: boolean;
/** Preserve leading zeros in ranges (passed to fill-range) */
strictZeros?: boolean;
}
/**
* Abstract Syntax Tree node structure
*/
interface BraceAST {
/** Node type identifier */
type: 'root' | 'brace' | 'text' | 'comma' | 'open' | 'close' | 'range';
/** Original input string (root nodes only) */
input?: string;
/** Node value/content */
value?: string;
/** Child nodes */
nodes?: BraceAST[];
/** Whether node is invalid */
invalid?: boolean;
/** Whether node is escaped */
escaped?: boolean;
}Braces supports all standard Bash 4.3 brace expansion patterns:
Lists (Sets): Comma-separated alternatives
braces('{a,b,c}'); // => ['(a|b|c)']
braces('{foo,bar,baz}'); // => ['(foo|bar|baz)']Sequences (Ranges): Numeric or alphabetic ranges
braces('{1..5}'); // => ['([1-5])']
braces('{a..e}'); // => ['([a-e])']
braces('{01..05}'); // => ['(0[1-5])'] (zero-padded)Stepped Ranges: Ranges with increments
braces('{2..10..2}'); // => ['(2|4|6|8|10)']
braces('{a..z..3}'); // => ['(a|d|g|j|m|p|s|v|y)']Nested Patterns: Complex nested brace expressions
braces('a/{x,{1..3},y}/b'); // => ['a/(x|([1-3])|y)/b']Escaping: Prevent evaluation of special characters
braces('a\\{b,c\\}d'); // => ['a{b,c}d'] (not expanded)Invalid Patterns: Patterns that don't follow proper brace syntax
braces('{a}'); // => ['{a}'] (single item, not expanded)
braces('{a,b'); // => ['{a,b'] (unclosed brace)
braces('a,b}'); // => ['a,b}'] (unopened brace)Brace patterns are considered invalid when they:
parse() is not a stringmaxLength limitrangeLimit during expansionFile path generation:
const paths = braces('./src/{components,utils,types}/**/*.js', { expand: true });
// => ['./src/components/**/*.js', './src/utils/**/*.js', './src/types/**/*.js']Regex pattern creation:
const pattern = braces('user-{200..300}/project-{a,b,c}')[0];
// => 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)'
const regex = new RegExp(pattern);Transform range values:
// For numeric ranges, 'value' is the number
const customRange = braces('{1..5}', {
expand: true,
transform: (value) => `item${value}`
});
// => ['item1', 'item2', 'item3', 'item4', 'item5']
// For alpha ranges, 'value' is a character code
const alphaRange = braces('{a..e}', {
expand: true,
transform: (code, index) => `${String.fromCharCode(code)}-${index}`
});
// => ['a-0', 'b-1', 'c-2', 'd-3', 'e-4']Safe processing with limits:
try {
const result = braces('{1..10000}', {
expand: true,
rangeLimit: 100
});
} catch (error) {
console.log('Range too large:', error.message);
// Use compile mode instead for large ranges
const compiled = braces('{1..10000}');
// => ['([1-9]|[1-9][0-9]{1,3}|10000)']
}