| /*! |
| * fill-range <https://github.com/jonschlinkert/fill-range> |
| * |
| * Copyright (c) 2014-present, Jon Schlinkert. |
| * Licensed under the MIT License. |
| */ |
| |
| 'use strict'; |
| |
| const util = require('util'); |
| const toRegexRange = require('to-regex-range'); |
| |
| const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); |
| |
| const transform = toNumber => { |
| return value => toNumber === true ? Number(value) : String(value); |
| }; |
| |
| const isValidValue = value => { |
| return typeof value === 'number' || (typeof value === 'string' && value !== ''); |
| }; |
| |
| const isNumber = num => Number.isInteger(+num); |
| |
| const zeros = input => { |
| let value = `${input}`; |
| let index = -1; |
| if (value[0] === '-') value = value.slice(1); |
| if (value === '0') return false; |
| while (value[++index] === '0'); |
| return index > 0; |
| }; |
| |
| const stringify = (start, end, options) => { |
| if (typeof start === 'string' || typeof end === 'string') { |
| return true; |
| } |
| return options.stringify === true; |
| }; |
| |
| const pad = (input, maxLength, toNumber) => { |
| if (maxLength > 0) { |
| let dash = input[0] === '-' ? '-' : ''; |
| if (dash) input = input.slice(1); |
| input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); |
| } |
| if (toNumber === false) { |
| return String(input); |
| } |
| return input; |
| }; |
| |
| const toMaxLen = (input, maxLength) => { |
| let negative = input[0] === '-' ? '-' : ''; |
| if (negative) { |
| input = input.slice(1); |
| maxLength--; |
| } |
| while (input.length < maxLength) input = '0' + input; |
| return negative ? ('-' + input) : input; |
| }; |
| |
| const toSequence = (parts, options) => { |
| parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); |
| parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); |
| |
| let prefix = options.capture ? '' : '?:'; |
| let positives = ''; |
| let negatives = ''; |
| let result; |
| |
| if (parts.positives.length) { |
| positives = parts.positives.join('|'); |
| } |
| |
| if (parts.negatives.length) { |
| negatives = `-(${prefix}${parts.negatives.join('|')})`; |
| } |
| |
| if (positives && negatives) { |
| result = `${positives}|${negatives}`; |
| } else { |
| result = positives || negatives; |
| } |
| |
| if (options.wrap) { |
| return `(${prefix}${result})`; |
| } |
| |
| return result; |
| }; |
| |
| const toRange = (a, b, isNumbers, options) => { |
| if (isNumbers) { |
| return toRegexRange(a, b, { wrap: false, ...options }); |
| } |
| |
| let start = String.fromCharCode(a); |
| if (a === b) return start; |
| |
| let stop = String.fromCharCode(b); |
| return `[${start}-${stop}]`; |
| }; |
| |
| const toRegex = (start, end, options) => { |
| if (Array.isArray(start)) { |
| let wrap = options.wrap === true; |
| let prefix = options.capture ? '' : '?:'; |
| return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); |
| } |
| return toRegexRange(start, end, options); |
| }; |
| |
| const rangeError = (...args) => { |
| return new RangeError('Invalid range arguments: ' + util.inspect(...args)); |
| }; |
| |
| const invalidRange = (start, end, options) => { |
| if (options.strictRanges === true) throw rangeError([start, end]); |
| return []; |
| }; |
| |
| const invalidStep = (step, options) => { |
| if (options.strictRanges === true) { |
| throw new TypeError(`Expected step "${step}" to be a number`); |
| } |
| return []; |
| }; |
| |
| const fillNumbers = (start, end, step = 1, options = {}) => { |
| let a = Number(start); |
| let b = Number(end); |
| |
| if (!Number.isInteger(a) || !Number.isInteger(b)) { |
| if (options.strictRanges === true) throw rangeError([start, end]); |
| return []; |
| } |
| |
| // fix negative zero |
| if (a === 0) a = 0; |
| if (b === 0) b = 0; |
| |
| let descending = a > b; |
| let startString = String(start); |
| let endString = String(end); |
| let stepString = String(step); |
| step = Math.max(Math.abs(step), 1); |
| |
| let padded = zeros(startString) || zeros(endString) || zeros(stepString); |
| let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; |
| let toNumber = padded === false && stringify(start, end, options) === false; |
| let format = options.transform || transform(toNumber); |
| |
| if (options.toRegex && step === 1) { |
| return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); |
| } |
| |
| let parts = { negatives: [], positives: [] }; |
| let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); |
| let range = []; |
| let index = 0; |
| |
| while (descending ? a >= b : a <= b) { |
| if (options.toRegex === true && step > 1) { |
| push(a); |
| } else { |
| range.push(pad(format(a, index), maxLen, toNumber)); |
| } |
| a = descending ? a - step : a + step; |
| index++; |
| } |
| |
| if (options.toRegex === true) { |
| return step > 1 |
| ? toSequence(parts, options) |
| : toRegex(range, null, { wrap: false, ...options }); |
| } |
| |
| return range; |
| }; |
| |
| const fillLetters = (start, end, step = 1, options = {}) => { |
| if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { |
| return invalidRange(start, end, options); |
| } |
| |
| |
| let format = options.transform || (val => String.fromCharCode(val)); |
| let a = `${start}`.charCodeAt(0); |
| let b = `${end}`.charCodeAt(0); |
| |
| let descending = a > b; |
| let min = Math.min(a, b); |
| let max = Math.max(a, b); |
| |
| if (options.toRegex && step === 1) { |
| return toRange(min, max, false, options); |
| } |
| |
| let range = []; |
| let index = 0; |
| |
| while (descending ? a >= b : a <= b) { |
| range.push(format(a, index)); |
| a = descending ? a - step : a + step; |
| index++; |
| } |
| |
| if (options.toRegex === true) { |
| return toRegex(range, null, { wrap: false, options }); |
| } |
| |
| return range; |
| }; |
| |
| const fill = (start, end, step, options = {}) => { |
| if (end == null && isValidValue(start)) { |
| return [start]; |
| } |
| |
| if (!isValidValue(start) || !isValidValue(end)) { |
| return invalidRange(start, end, options); |
| } |
| |
| if (typeof step === 'function') { |
| return fill(start, end, 1, { transform: step }); |
| } |
| |
| if (isObject(step)) { |
| return fill(start, end, 0, step); |
| } |
| |
| let opts = { ...options }; |
| if (opts.capture === true) opts.wrap = true; |
| step = step || opts.step || 1; |
| |
| if (!isNumber(step)) { |
| if (step != null && !isObject(step)) return invalidStep(step, opts); |
| return fill(start, end, 1, step); |
| } |
| |
| if (isNumber(start) && isNumber(end)) { |
| return fillNumbers(start, end, step, opts); |
| } |
| |
| return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); |
| }; |
| |
| module.exports = fill; |