123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*!
- * fill-range <https://github.com/jonschlinkert/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;
|