UNPKG

@feugene/mu

Version:

Helpful TS utilities without dependencies

120 lines 4.65 kB
import isNil from '../is/isNil.mjs'; import isNumeric from '../is/isNumeric.mjs'; const queryRe = /^\?/; const plusRe = /\+/g; const keyRe = /(\[):?([^\]]*)\]/g; const nameRe = /^([^[]+)/; // eslint-disable-line no-useless-escape /** * Converts a query string back into an object. * * Non-recursive: * * fromQueryString("foo=1&bar=2"); // returns {foo: '1', bar: '2'} * fromQueryString("foo=&bar=2"); // returns {foo: '', bar: '2'} * fromQueryString("some%20price=%24300"); // returns {'some price': '$300'} * fromQueryString("colors=red&colors=green&colors=blue"); // returns {colors: ['red', 'green', 'blue']} * * Recursive: * * fromQueryString( * "username=Jacky&"+ * "dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&"+ * "hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&"+ * "hobbies[3][0]=nested&hobbies[3][1]=stuff", true); * * // returns * { * username: 'Jacky', * dateOfBirth: { * day: '1', * month: '2', * year: '1911' * }, * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']] * } * * @param {String|null} queryString The query string to decode * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by * @param {Object} options = { * - decodeName {Boolean} Decode KeyNames in the queryString * } * PHP / Ruby on Rails servers and similar. * @return {Object} * @todo write tests */ export default function fromQueryString(queryString, recursive = false, options = { decodeName: true }) { if (isNil(queryString)) { return {}; } let parts = queryString.replace(queryRe, '').split('&'), object = Object.create(null), temporary, components, name, value, i, ln, part, j, subLn, matchedKeys, matchedName, keys, key, nextKey; for (i = 0, ln = parts.length; i < ln; i++) { part = parts[i]; if (part.length > 0) { components = part.split('='); name = components[0]; name = name.replace(plusRe, '%20'); name = options.decodeName ? decodeURIComponent(name) : name; value = components[1]; if (value !== undefined) { value = value.replace(plusRe, '%20'); value = decodeURIComponent(value); } else { value = ''; } if (!recursive) { if (Object.prototype.hasOwnProperty.call(object, name)) { if (!Array.isArray(object[name])) { object[name] = [object[name]]; } object[name].push(value); } else { object[name] = value; } } else { matchedKeys = name.match(keyRe); matchedName = name.match(nameRe); //<debug> if (!matchedName) { throw new Error('[fromQueryString] Malformed query string given, failed parsing name from "' + part + '"'); } //</debug> name = matchedName[0]; keys = []; if (matchedKeys === null) { object[name] = value; continue; } for (j = 0, subLn = matchedKeys.length; j < subLn; j++) { key = matchedKeys[j]; key = key.length === 2 ? '' : key.substring(1, key.length - 1); keys.push(key); } keys.unshift(name); temporary = object; for (j = 0, subLn = keys.length; j < subLn; j++) { key = keys[j]; if (j === subLn - 1) { if (Array.isArray(temporary) && key === '') { temporary.push(value); } else { temporary[key] = value; } } else { if (temporary[key] === undefined || typeof temporary[key] === 'string') { nextKey = keys[j + 1]; temporary[key] = isNumeric(nextKey) || nextKey === '' ? [] : {}; } temporary = temporary[key]; } } } } } return object; } //# sourceMappingURL=fromQueryString.mjs.map