UNPKG

@kitmi/utils

Version:

A JavaScript utility library for both server and browser

105 lines (93 loc) 3.61 kB
import { typeOf } from '@kitmi/types'; /** * Function arguments extractor. * @param {Array} args - Array of function arguments * @param {Array} types - Array of types, with suffix `?` as optional * @example * function withClient(...args) { * const [server, authenticator, testToRun, options] = fxargs(args, ['string?', 'string?', 'function', 'object?']); * // ... * } * * 1. withClient('http://localhost:3000', 'admin', (app) => {}); * - server: 'http://localhost:3000' * - authenticator: 'admin' * - testToRun: (app) => {} * - options: undefined * * 2. withClient('http://localhost:3000', (app) => {}); * - server: 'http://localhost:3000' * - authenticator: undefined * - testToRun: (app) => {} * - options: undefined * * 3. withClient((app) => {}); * - server: undefined * - authenticator: undefined * - testToRun: (app) => {} * - options: undefined */ function fxargs(args, types) { // eslint-disable-next-line no-new-array const result = new Array(types.length).fill(undefined); let argIndex = 0, lt = types.length, la = args.length; const optionalSpots = []; for (let i = 0; i < lt; i++) { const type = types[i]; const isOptional = type.endsWith('?'); const typeOptions = isOptional ? type.slice(0, -1).split('|') : type.split('|'); let matched = false; // Iterate over remaining args to find a match while (argIndex < la && !matched) { const value = args[argIndex]; if (typeOptions.some((typeOption) => typeOf(value) === typeOption)) { // Assign the value and increment the argument index result[i] = value; argIndex++; matched = true; if (isOptional) { optionalSpots.push(i); } else if ( optionalSpots.length > 0 && typeOptions.every((typeOption) => typeOf(args[argIndex - 2]) !== typeOption) ) { optionalSpots.length = 0; } } else if (isOptional) { // If the type is optional and no match found, break to allow for undefined break; } else { // Try pop up an optional value if (optionalSpots.length > 0 && lt - i >= la - argIndex + 1) { const optionalIndex = optionalSpots.pop(); for (let j = optionalIndex + 1; j < i; j++) { result[j] = result[j - 1]; } result[optionalIndex] = undefined; argIndex--; continue; } // No match found for a required type throw new Error(`Missing or invalid argument at index ${i}, expected "${type}".`); } } if (!matched && !isOptional) { // Try pop up an optional value 2, 2- if (optionalSpots.length > 0 && lt - i >= la - argIndex + 1) { const optionalIndex = optionalSpots.pop(); for (let j = optionalIndex + 1; j < i; j++) { result[j] = result[j - 1]; } result[optionalIndex] = undefined; argIndex--; i--; } else { throw new Error(`Missing argument at index ${i}, expected "${type}".`); } } } return result; } export default fxargs;