@bluelovers/fill-range
Version:
Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`
357 lines (270 loc) • 8.67 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('util'), require('@bluelovers/to-regex-range2')) :
typeof define === 'function' && define.amd ? define(['exports', 'util', '@bluelovers/to-regex-range2'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BlueloversFillRange = {}, global.util, global.toRegexRange2));
})(this, (function (exports, util, toRegexRange2) { 'use strict';
var EnumNegative;
(function (EnumNegative) {
EnumNegative["negative"] = "-";
EnumNegative["none"] = "";
})(EnumNegative || (EnumNegative = {}));
function isObject(val) {
return val !== null && typeof val === 'object' && !Array.isArray(val);
}
const transform = toNumber => {
if (toNumber === true) return value => Number(value);
return 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) {
input = toMaxLen(input, maxLength);
}
if (toNumber === false) {
return String(input);
}
return input;
};
const toMaxLen = (input, _maxLength) => {
let {
result,
negative,
maxLength
} = _prefixNegative(input, _maxLength);
return negative + result.padStart(maxLength, '0');
};
function _partsSort(part) {
part.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
}
function _partsCapturePrefix(options) {
return options.capture ? '' : '?:';
}
function _prefixNegative(input, maxLength) {
const negative = input[0] === "-" ? "-" : "";
if (negative === "-") {
input = input.slice(1);
maxLength--;
}
return {
result: input,
negative,
maxLength
};
}
function _join(part) {
return part.join('|');
}
const toSequence = (parts, options) => {
_partsSort(parts.negatives);
_partsSort(parts.positives);
let prefix = _partsCapturePrefix(options);
let positives = '';
let negatives = '';
let result;
if (parts.positives.length) {
positives = _join(parts.positives);
}
if (parts.negatives.length) {
negatives = `-(${prefix}${_join(parts.negatives)})`;
}
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 toRegexRange2.toRegexRange(a, b, {
wrap: false,
...options
});
}
const start = String.fromCharCode(a);
if (a === b) return start;
const stop = String.fromCharCode(b);
return `[${start}-${stop}]`;
};
const toRegex = (start, end, options) => {
if (Array.isArray(start)) {
const wrap = options.wrap === true;
const prefix = _partsCapturePrefix(options);
start = _join(start);
return wrap ? `(${prefix}${start})` : start;
}
return toRegexRange2.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], options);
return [];
};
const invalidStep = (step, options) => {
if (options.strictRanges === true) {
throw new TypeError(`Expected step "${step}" to be a number`);
}
return [];
};
function _handleLimit(options) {
return options.limit > 0 ? options.limit : Infinity;
}
function _handleStep(step) {
return Math.max(Math.abs(step), 1);
}
function _handleOptions(opts, clone) {
if (clone === true) {
opts = { ...opts
};
}
if (opts.capture === true) opts.wrap = true;
return opts;
}
function _handleDescending(start, end, options) {
const descending = start > end;
if (descending === true && options.strictOrder) {
throw rangeError([start, end], options);
}
return descending;
}
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], options);
return [];
}
if (a === 0) a = 0;
if (b === 0) b = 0;
const descending = _handleDescending(a, b, options);
const startString = String(start);
const endString = String(end);
const stepString = String(step);
step = _handleStep(step);
const padded = zeros(startString) || zeros(endString) || zeros(stepString);
const maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
const toNumber = padded === false && stringify(start, end, options) === false;
const format = options.transform || transform(toNumber);
if (options.toRegex && step === 1) {
return toRange(toMaxLen(String(start), maxLen), toMaxLen(String(end), maxLen), true, options);
}
const parts = {
negatives: [],
positives: []
};
const push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
const range = [];
let index = 0;
const limit = _handleLimit(options);
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 (index >= limit) break;
}
if (options.toRegex === true) {
return step > 1 ? toSequence(parts, options) : toRegex(range, null, {
wrap: false,
...options
});
}
return range;
};
function fillLetters(start, end, step = 1, options = {}) {
if (!isNumber(start) && start.length > 1 || !isNumber(end) && end.length > 1) {
return invalidRange(start, end, options);
}
const format = options.transform || (val => String.fromCharCode(val));
let a = `${start}`.charCodeAt(0);
let b = `${end}`.charCodeAt(0);
const descending = _handleDescending(a, b, options);
const min = Math.min(a, b);
const max = Math.max(a, b);
if (options.toRegex === true && step === 1) {
return toRange(min, max, false, options);
}
const range = [];
let index = 0;
const limit = _handleLimit(options);
while (descending ? a >= b : a <= b) {
range.push(format(a, index));
a = descending ? a - step : a + step;
index++;
if (index >= limit) break;
}
if (options.toRegex === true) {
return toRegex(range, null, {
wrap: false,
...options
});
}
return range;
}
function fill(start, end, step, options = {}) {
const _s = isValidValue(start);
if ((typeof end === 'undefined' || end === null) && _s) {
return [start];
}
if (!_s || !isValidValue(end)) {
return invalidRange(start, end, options);
}
if (typeof step === 'function') {
[step, options] = [1, {
transform: step
}];
}
if (isObject(step)) {
[step, options] = [0, step];
}
let opts = options;
step = step || opts.step || 1;
if (!isNumber(step)) {
if (step != null && !isObject(step)) return invalidStep(step, opts);
[step, opts] = [1, opts];
}
opts = _handleOptions(opts, true);
if (isNumber(start) && isNumber(end)) {
return fillNumbers(start, end, step, opts);
}
return fillLetters(start, end, _handleStep(step), opts);
}
Object.defineProperty(fill, '__esModule', {
value: true
});
Object.defineProperty(fill, 'fill', {
value: fill
});
Object.defineProperty(fill, 'default', {
value: fill
});
exports["default"] = fill;
exports.fill = fill;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=index.umd.development.cjs.map