expand.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. 'use strict';
  2. const fill = require('fill-range');
  3. const stringify = require('./stringify');
  4. const utils = require('./utils');
  5. const append = (queue = '', stash = '', enclose = false) => {
  6. const result = [];
  7. queue = [].concat(queue);
  8. stash = [].concat(stash);
  9. if (!stash.length) return queue;
  10. if (!queue.length) {
  11. return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash;
  12. }
  13. for (const item of queue) {
  14. if (Array.isArray(item)) {
  15. for (const value of item) {
  16. result.push(append(value, stash, enclose));
  17. }
  18. } else {
  19. for (let ele of stash) {
  20. if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
  21. result.push(Array.isArray(ele) ? append(item, ele, enclose) : item + ele);
  22. }
  23. }
  24. }
  25. return utils.flatten(result);
  26. };
  27. const expand = (ast, options = {}) => {
  28. const rangeLimit = options.rangeLimit === undefined ? 1000 : options.rangeLimit;
  29. const walk = (node, parent = {}) => {
  30. node.queue = [];
  31. let p = parent;
  32. let q = parent.queue;
  33. while (p.type !== 'brace' && p.type !== 'root' && p.parent) {
  34. p = p.parent;
  35. q = p.queue;
  36. }
  37. if (node.invalid || node.dollar) {
  38. q.push(append(q.pop(), stringify(node, options)));
  39. return;
  40. }
  41. if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) {
  42. q.push(append(q.pop(), ['{}']));
  43. return;
  44. }
  45. if (node.nodes && node.ranges > 0) {
  46. const args = utils.reduce(node.nodes);
  47. if (utils.exceedsLimit(...args, options.step, rangeLimit)) {
  48. throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
  49. }
  50. let range = fill(...args, options);
  51. if (range.length === 0) {
  52. range = stringify(node, options);
  53. }
  54. q.push(append(q.pop(), range));
  55. node.nodes = [];
  56. return;
  57. }
  58. const enclose = utils.encloseBrace(node);
  59. let queue = node.queue;
  60. let block = node;
  61. while (block.type !== 'brace' && block.type !== 'root' && block.parent) {
  62. block = block.parent;
  63. queue = block.queue;
  64. }
  65. for (let i = 0; i < node.nodes.length; i++) {
  66. const child = node.nodes[i];
  67. if (child.type === 'comma' && node.type === 'brace') {
  68. if (i === 1) queue.push('');
  69. queue.push('');
  70. continue;
  71. }
  72. if (child.type === 'close') {
  73. q.push(append(q.pop(), queue, enclose));
  74. continue;
  75. }
  76. if (child.value && child.type !== 'open') {
  77. queue.push(append(queue.pop(), child.value));
  78. continue;
  79. }
  80. if (child.nodes) {
  81. walk(child, node);
  82. }
  83. }
  84. return queue;
  85. };
  86. return utils.flatten(walk(ast));
  87. };
  88. module.exports = expand;