| 'use strict'; |
| |
| const fill = require('fill-range'); |
| const stringify = require('./stringify'); |
| const utils = require('./utils'); |
| |
| const append = (queue = '', stash = '', enclose = false) => { |
| let result = []; |
| |
| queue = [].concat(queue); |
| stash = [].concat(stash); |
| |
| if (!stash.length) return queue; |
| if (!queue.length) { |
| return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash; |
| } |
| |
| for (let item of queue) { |
| if (Array.isArray(item)) { |
| for (let value of item) { |
| result.push(append(value, stash, enclose)); |
| } |
| } else { |
| for (let ele of stash) { |
| if (enclose === true && typeof ele === 'string') ele = `{${ele}}`; |
| result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele)); |
| } |
| } |
| } |
| return utils.flatten(result); |
| }; |
| |
| const expand = (ast, options = {}) => { |
| let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit; |
| |
| let walk = (node, parent = {}) => { |
| node.queue = []; |
| |
| let p = parent; |
| let q = parent.queue; |
| |
| while (p.type !== 'brace' && p.type !== 'root' && p.parent) { |
| p = p.parent; |
| q = p.queue; |
| } |
| |
| if (node.invalid || node.dollar) { |
| q.push(append(q.pop(), stringify(node, options))); |
| return; |
| } |
| |
| if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) { |
| q.push(append(q.pop(), ['{}'])); |
| return; |
| } |
| |
| if (node.nodes && node.ranges > 0) { |
| let args = utils.reduce(node.nodes); |
| |
| if (utils.exceedsLimit(...args, options.step, rangeLimit)) { |
| throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); |
| } |
| |
| let range = fill(...args, options); |
| if (range.length === 0) { |
| range = stringify(node, options); |
| } |
| |
| q.push(append(q.pop(), range)); |
| node.nodes = []; |
| return; |
| } |
| |
| let enclose = utils.encloseBrace(node); |
| let queue = node.queue; |
| let block = node; |
| |
| while (block.type !== 'brace' && block.type !== 'root' && block.parent) { |
| block = block.parent; |
| queue = block.queue; |
| } |
| |
| for (let i = 0; i < node.nodes.length; i++) { |
| let child = node.nodes[i]; |
| |
| if (child.type === 'comma' && node.type === 'brace') { |
| if (i === 1) queue.push(''); |
| queue.push(''); |
| continue; |
| } |
| |
| if (child.type === 'close') { |
| q.push(append(q.pop(), queue, enclose)); |
| continue; |
| } |
| |
| if (child.value && child.type !== 'open') { |
| queue.push(append(queue.pop(), child.value)); |
| continue; |
| } |
| |
| if (child.nodes) { |
| walk(child, node); |
| } |
| } |
| |
| return queue; |
| }; |
| |
| return utils.flatten(walk(ast)); |
| }; |
| |
| module.exports = expand; |