/*! * fill-range * * Copyright (c) 2014-2015, 2017, Jon Schlinkert. * Released under the MIT License. */ 'use strict'; var util = require('util'); var isNumber = require('is-number'); var extend = require('extend-shallow'); var repeat = require('repeat-string'); var toRegex = require('to-regex-range'); /** * Return a range of numbers or letters. * * @param {String} `start` Start of the range * @param {String} `stop` End of the range * @param {String} `step` Increment or decrement to use. * @param {Function} `fn` Custom function to modify each element in the range. * @return {Array} */ function fillRange(start, stop, step, options) { if (typeof start === 'undefined') { return []; } if (typeof stop === 'undefined' || start === stop) { // special case, for handling negative zero var isString = typeof start === 'string'; if (isNumber(start) && !toNumber(start)) { return [isString ? '0' : 0]; } return [start]; } if (typeof step !== 'number' && typeof step !== 'string') { options = step; step = undefined; } if (typeof options === 'function') { options = { transform: options }; } var opts = extend({step: step}, options); if (opts.step && !isValidNumber(opts.step)) { if (opts.strictRanges === true) { throw new TypeError('expected options.step to be a number'); } return []; } opts.isNumber = isValidNumber(start) && isValidNumber(stop); if (!opts.isNumber && !isValid(start, stop)) { if (opts.strictRanges === true) { throw new RangeError('invalid range arguments: ' + util.inspect([start, stop])); } return []; } opts.isPadded = isPadded(start) || isPadded(stop); opts.toString = opts.stringify || typeof opts.step === 'string' || typeof start === 'string' || typeof stop === 'string' || !opts.isNumber; if (opts.isPadded) { opts.maxLength = Math.max(String(start).length, String(stop).length); } // support legacy minimatch/fill-range options if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize; if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe; return expand(start, stop, opts); } function expand(start, stop, options) { var a = options.isNumber ? toNumber(start) : start.charCodeAt(0); var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0); var step = Math.abs(toNumber(options.step)) || 1; if (options.toRegex && step === 1) { return toRange(a, b, start, stop, options); } var zero = {greater: [], lesser: []}; var asc = a < b; var arr = new Array(Math.round((asc ? b - a : a - b) / step)); var idx = 0; while (asc ? a <= b : a >= b) { var val = options.isNumber ? a : String.fromCharCode(a); if (options.toRegex && (val >= 0 || !options.isNumber)) { zero.greater.push(val); } else { zero.lesser.push(Math.abs(val)); } if (options.isPadded) { val = zeros(val, options); } if (options.toString) { val = String(val); } if (typeof options.transform === 'function') { arr[idx++] = options.transform(val, a, b, step, idx, arr, options); } else { arr[idx++] = val; } if (asc) { a += step; } else { a -= step; } } if (options.toRegex === true) { return toSequence(arr, zero, options); } return arr; } function toRange(a, b, start, stop, options) { if (options.isPadded) { return toRegex(start, stop, options); } if (options.isNumber) { return toRegex(Math.min(a, b), Math.max(a, b), options); } var start = String.fromCharCode(Math.min(a, b)); var stop = String.fromCharCode(Math.max(a, b)); return '[' + start + '-' + stop + ']'; } function toSequence(arr, zeros, options) { var greater = '', lesser = ''; if (zeros.greater.length) { greater = zeros.greater.join('|'); } if (zeros.lesser.length) { lesser = '-(' + zeros.lesser.join('|') + ')'; } var res = greater && lesser ? greater + '|' + lesser : greater || lesser; if (options.capture) { return '(' + res + ')'; } return res; } function zeros(val, options) { if (options.isPadded) { var str = String(val); var len = str.length; var dash = ''; if (str.charAt(0) === '-') { dash = '-'; str = str.slice(1); } var diff = options.maxLength - len; var pad = repeat('0', diff); val = (dash + pad + str); } if (options.stringify) { return String(val); } return val; } function toNumber(val) { return Number(val) || 0; } function isPadded(str) { return /^-?0\d/.test(str); } function isValid(min, max) { return (isValidNumber(min) || isValidLetter(min)) && (isValidNumber(max) || isValidLetter(max)); } function isValidLetter(ch) { return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch); } function isValidNumber(n) { return isNumber(n) && !/\./.test(n); } /** * Expose `fillRange` * @type {Function} */ module.exports = fillRange;