initial commit
[squeep-querystring] / lib / parse.js
1 /* eslint-disable security/detect-object-injection */
2 'use strict';
3
4 const SPACES = /\+/ug;
5 const DELIM = /[&;]/u;
6
7 /**
8 * Separate a string into decoded key and value.
9 * @param {string} param
10 * @returns {object} .key
11 * .val
12 */
13 const splitKeyValue = (param) => {
14 const idx = param.indexOf('=');
15 let key, val;
16 if (idx >= 0) {
17 key = decodeURIComponent(param.slice(0, idx));
18 val = decodeURIComponent(param.slice(idx + 1));
19 } else {
20 key = decodeURIComponent(param);
21 val = null;
22 }
23 return {
24 key,
25 val,
26 };
27 };
28
29 /**
30 * Parse a raw querystring (as provided by url.parse) into a map of
31 * parameter values.
32 * This is nearly the same as the node-provided querystring.parse,
33 * but is more accepting of legacy conventions and (subjectively) more convenient in output.
34 * Primary differences:
35 * for valueless keys, value is set to null, not ''
36 * ; is a valid parameter separator
37 * empty parameters are included as a '' key
38 *
39 * @param {string} querystring - raw query string
40 * @param {boolean} arraySuffix - allow param[] to explicitly treat param as array, à la PHP
41 * @returns {object} - parsed data
42 */
43 function parse(querystring, arraySuffix=false) {
44 const params = {};
45 if (querystring) {
46 const queries = querystring.replace(SPACES, '%20').split(DELIM);
47 queries.forEach((query) => {
48 // eslint-disable-next-line prefer-const
49 let { key, val } = splitKeyValue(query);
50
51 if (arraySuffix
52 && key.endsWith('[]')) {
53 key = key.slice(0, key.length - 2);
54 if (!Object.prototype.hasOwnProperty.call(params, key)) {
55 params[key] = Array();
56 }
57 }
58
59 if (!Object.prototype.hasOwnProperty.call(params, key)) {
60 params[key] = val;
61 } else if (Array.isArray(params[key])) {
62 params[key].push(val);
63 } else {
64 params[key] = Array(params[key], val);
65 }
66 });
67 }
68 return params;
69 }
70
71 module.exports = {
72 parse,
73 };