123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- /*!
- * 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, maxLen) => {
- 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.map(v => toMaxLen(String(v), maxLen)).join('|');
- }
- if (parts.negatives.length) {
- negatives = `-(${prefix}${parts.negatives.map(v => toMaxLen(String(v), maxLen)).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, maxLen)
- : 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;
|