[6a3a178] | 1 | /*!
|
---|
| 2 | * fill-range <https://github.com/jonschlinkert/fill-range>
|
---|
| 3 | *
|
---|
| 4 | * Copyright (c) 2014-present, Jon Schlinkert.
|
---|
| 5 | * Licensed under the MIT License.
|
---|
| 6 | */
|
---|
| 7 |
|
---|
| 8 | 'use strict';
|
---|
| 9 |
|
---|
| 10 | const util = require('util');
|
---|
| 11 | const toRegexRange = require('to-regex-range');
|
---|
| 12 |
|
---|
| 13 | const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
|
---|
| 14 |
|
---|
| 15 | const transform = toNumber => {
|
---|
| 16 | return value => toNumber === true ? Number(value) : String(value);
|
---|
| 17 | };
|
---|
| 18 |
|
---|
| 19 | const isValidValue = value => {
|
---|
| 20 | return typeof value === 'number' || (typeof value === 'string' && value !== '');
|
---|
| 21 | };
|
---|
| 22 |
|
---|
| 23 | const isNumber = num => Number.isInteger(+num);
|
---|
| 24 |
|
---|
| 25 | const zeros = input => {
|
---|
| 26 | let value = `${input}`;
|
---|
| 27 | let index = -1;
|
---|
| 28 | if (value[0] === '-') value = value.slice(1);
|
---|
| 29 | if (value === '0') return false;
|
---|
| 30 | while (value[++index] === '0');
|
---|
| 31 | return index > 0;
|
---|
| 32 | };
|
---|
| 33 |
|
---|
| 34 | const stringify = (start, end, options) => {
|
---|
| 35 | if (typeof start === 'string' || typeof end === 'string') {
|
---|
| 36 | return true;
|
---|
| 37 | }
|
---|
| 38 | return options.stringify === true;
|
---|
| 39 | };
|
---|
| 40 |
|
---|
| 41 | const pad = (input, maxLength, toNumber) => {
|
---|
| 42 | if (maxLength > 0) {
|
---|
| 43 | let dash = input[0] === '-' ? '-' : '';
|
---|
| 44 | if (dash) input = input.slice(1);
|
---|
| 45 | input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0'));
|
---|
| 46 | }
|
---|
| 47 | if (toNumber === false) {
|
---|
| 48 | return String(input);
|
---|
| 49 | }
|
---|
| 50 | return input;
|
---|
| 51 | };
|
---|
| 52 |
|
---|
| 53 | const toMaxLen = (input, maxLength) => {
|
---|
| 54 | let negative = input[0] === '-' ? '-' : '';
|
---|
| 55 | if (negative) {
|
---|
| 56 | input = input.slice(1);
|
---|
| 57 | maxLength--;
|
---|
| 58 | }
|
---|
| 59 | while (input.length < maxLength) input = '0' + input;
|
---|
| 60 | return negative ? ('-' + input) : input;
|
---|
| 61 | };
|
---|
| 62 |
|
---|
| 63 | const toSequence = (parts, options) => {
|
---|
| 64 | parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
---|
| 65 | parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
---|
| 66 |
|
---|
| 67 | let prefix = options.capture ? '' : '?:';
|
---|
| 68 | let positives = '';
|
---|
| 69 | let negatives = '';
|
---|
| 70 | let result;
|
---|
| 71 |
|
---|
| 72 | if (parts.positives.length) {
|
---|
| 73 | positives = parts.positives.join('|');
|
---|
| 74 | }
|
---|
| 75 |
|
---|
| 76 | if (parts.negatives.length) {
|
---|
| 77 | negatives = `-(${prefix}${parts.negatives.join('|')})`;
|
---|
| 78 | }
|
---|
| 79 |
|
---|
| 80 | if (positives && negatives) {
|
---|
| 81 | result = `${positives}|${negatives}`;
|
---|
| 82 | } else {
|
---|
| 83 | result = positives || negatives;
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | if (options.wrap) {
|
---|
| 87 | return `(${prefix}${result})`;
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | return result;
|
---|
| 91 | };
|
---|
| 92 |
|
---|
| 93 | const toRange = (a, b, isNumbers, options) => {
|
---|
| 94 | if (isNumbers) {
|
---|
| 95 | return toRegexRange(a, b, { wrap: false, ...options });
|
---|
| 96 | }
|
---|
| 97 |
|
---|
| 98 | let start = String.fromCharCode(a);
|
---|
| 99 | if (a === b) return start;
|
---|
| 100 |
|
---|
| 101 | let stop = String.fromCharCode(b);
|
---|
| 102 | return `[${start}-${stop}]`;
|
---|
| 103 | };
|
---|
| 104 |
|
---|
| 105 | const toRegex = (start, end, options) => {
|
---|
| 106 | if (Array.isArray(start)) {
|
---|
| 107 | let wrap = options.wrap === true;
|
---|
| 108 | let prefix = options.capture ? '' : '?:';
|
---|
| 109 | return wrap ? `(${prefix}${start.join('|')})` : start.join('|');
|
---|
| 110 | }
|
---|
| 111 | return toRegexRange(start, end, options);
|
---|
| 112 | };
|
---|
| 113 |
|
---|
| 114 | const rangeError = (...args) => {
|
---|
| 115 | return new RangeError('Invalid range arguments: ' + util.inspect(...args));
|
---|
| 116 | };
|
---|
| 117 |
|
---|
| 118 | const invalidRange = (start, end, options) => {
|
---|
| 119 | if (options.strictRanges === true) throw rangeError([start, end]);
|
---|
| 120 | return [];
|
---|
| 121 | };
|
---|
| 122 |
|
---|
| 123 | const invalidStep = (step, options) => {
|
---|
| 124 | if (options.strictRanges === true) {
|
---|
| 125 | throw new TypeError(`Expected step "${step}" to be a number`);
|
---|
| 126 | }
|
---|
| 127 | return [];
|
---|
| 128 | };
|
---|
| 129 |
|
---|
| 130 | const fillNumbers = (start, end, step = 1, options = {}) => {
|
---|
| 131 | let a = Number(start);
|
---|
| 132 | let b = Number(end);
|
---|
| 133 |
|
---|
| 134 | if (!Number.isInteger(a) || !Number.isInteger(b)) {
|
---|
| 135 | if (options.strictRanges === true) throw rangeError([start, end]);
|
---|
| 136 | return [];
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | // fix negative zero
|
---|
| 140 | if (a === 0) a = 0;
|
---|
| 141 | if (b === 0) b = 0;
|
---|
| 142 |
|
---|
| 143 | let descending = a > b;
|
---|
| 144 | let startString = String(start);
|
---|
| 145 | let endString = String(end);
|
---|
| 146 | let stepString = String(step);
|
---|
| 147 | step = Math.max(Math.abs(step), 1);
|
---|
| 148 |
|
---|
| 149 | let padded = zeros(startString) || zeros(endString) || zeros(stepString);
|
---|
| 150 | let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
|
---|
| 151 | let toNumber = padded === false && stringify(start, end, options) === false;
|
---|
| 152 | let format = options.transform || transform(toNumber);
|
---|
| 153 |
|
---|
| 154 | if (options.toRegex && step === 1) {
|
---|
| 155 | return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | let parts = { negatives: [], positives: [] };
|
---|
| 159 | let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
|
---|
| 160 | let range = [];
|
---|
| 161 | let index = 0;
|
---|
| 162 |
|
---|
| 163 | while (descending ? a >= b : a <= b) {
|
---|
| 164 | if (options.toRegex === true && step > 1) {
|
---|
| 165 | push(a);
|
---|
| 166 | } else {
|
---|
| 167 | range.push(pad(format(a, index), maxLen, toNumber));
|
---|
| 168 | }
|
---|
| 169 | a = descending ? a - step : a + step;
|
---|
| 170 | index++;
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | if (options.toRegex === true) {
|
---|
| 174 | return step > 1
|
---|
| 175 | ? toSequence(parts, options)
|
---|
| 176 | : toRegex(range, null, { wrap: false, ...options });
|
---|
| 177 | }
|
---|
| 178 |
|
---|
| 179 | return range;
|
---|
| 180 | };
|
---|
| 181 |
|
---|
| 182 | const fillLetters = (start, end, step = 1, options = {}) => {
|
---|
| 183 | if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) {
|
---|
| 184 | return invalidRange(start, end, options);
|
---|
| 185 | }
|
---|
| 186 |
|
---|
| 187 |
|
---|
| 188 | let format = options.transform || (val => String.fromCharCode(val));
|
---|
| 189 | let a = `${start}`.charCodeAt(0);
|
---|
| 190 | let b = `${end}`.charCodeAt(0);
|
---|
| 191 |
|
---|
| 192 | let descending = a > b;
|
---|
| 193 | let min = Math.min(a, b);
|
---|
| 194 | let max = Math.max(a, b);
|
---|
| 195 |
|
---|
| 196 | if (options.toRegex && step === 1) {
|
---|
| 197 | return toRange(min, max, false, options);
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | let range = [];
|
---|
| 201 | let index = 0;
|
---|
| 202 |
|
---|
| 203 | while (descending ? a >= b : a <= b) {
|
---|
| 204 | range.push(format(a, index));
|
---|
| 205 | a = descending ? a - step : a + step;
|
---|
| 206 | index++;
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | if (options.toRegex === true) {
|
---|
| 210 | return toRegex(range, null, { wrap: false, options });
|
---|
| 211 | }
|
---|
| 212 |
|
---|
| 213 | return range;
|
---|
| 214 | };
|
---|
| 215 |
|
---|
| 216 | const fill = (start, end, step, options = {}) => {
|
---|
| 217 | if (end == null && isValidValue(start)) {
|
---|
| 218 | return [start];
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | if (!isValidValue(start) || !isValidValue(end)) {
|
---|
| 222 | return invalidRange(start, end, options);
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | if (typeof step === 'function') {
|
---|
| 226 | return fill(start, end, 1, { transform: step });
|
---|
| 227 | }
|
---|
| 228 |
|
---|
| 229 | if (isObject(step)) {
|
---|
| 230 | return fill(start, end, 0, step);
|
---|
| 231 | }
|
---|
| 232 |
|
---|
| 233 | let opts = { ...options };
|
---|
| 234 | if (opts.capture === true) opts.wrap = true;
|
---|
| 235 | step = step || opts.step || 1;
|
---|
| 236 |
|
---|
| 237 | if (!isNumber(step)) {
|
---|
| 238 | if (step != null && !isObject(step)) return invalidStep(step, opts);
|
---|
| 239 | return fill(start, end, 1, step);
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | if (isNumber(start) && isNumber(end)) {
|
---|
| 243 | return fillNumbers(start, end, step, opts);
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | return fillLetters(start, end, Math.max(Math.abs(step), 1), opts);
|
---|
| 247 | };
|
---|
| 248 |
|
---|
| 249 | module.exports = fill;
|
---|