| Leo Repp | 58b9f11 | 2021-11-22 11:57:47 +0100 | [diff] [blame^] | 1 | 'use strict'; |
| 2 | var strictUriEncode = require('strict-uri-encode'); |
| 3 | var objectAssign = require('object-assign'); |
| 4 | var decodeComponent = require('decode-uri-component'); |
| 5 | |
| 6 | function encoderForArrayFormat(opts) { |
| 7 | switch (opts.arrayFormat) { |
| 8 | case 'index': |
| 9 | return function (key, value, index) { |
| 10 | return value === null ? [ |
| 11 | encode(key, opts), |
| 12 | '[', |
| 13 | index, |
| 14 | ']' |
| 15 | ].join('') : [ |
| 16 | encode(key, opts), |
| 17 | '[', |
| 18 | encode(index, opts), |
| 19 | ']=', |
| 20 | encode(value, opts) |
| 21 | ].join(''); |
| 22 | }; |
| 23 | |
| 24 | case 'bracket': |
| 25 | return function (key, value) { |
| 26 | return value === null ? encode(key, opts) : [ |
| 27 | encode(key, opts), |
| 28 | '[]=', |
| 29 | encode(value, opts) |
| 30 | ].join(''); |
| 31 | }; |
| 32 | |
| 33 | default: |
| 34 | return function (key, value) { |
| 35 | return value === null ? encode(key, opts) : [ |
| 36 | encode(key, opts), |
| 37 | '=', |
| 38 | encode(value, opts) |
| 39 | ].join(''); |
| 40 | }; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | function parserForArrayFormat(opts) { |
| 45 | var result; |
| 46 | |
| 47 | switch (opts.arrayFormat) { |
| 48 | case 'index': |
| 49 | return function (key, value, accumulator) { |
| 50 | result = /\[(\d*)\]$/.exec(key); |
| 51 | |
| 52 | key = key.replace(/\[\d*\]$/, ''); |
| 53 | |
| 54 | if (!result) { |
| 55 | accumulator[key] = value; |
| 56 | return; |
| 57 | } |
| 58 | |
| 59 | if (accumulator[key] === undefined) { |
| 60 | accumulator[key] = {}; |
| 61 | } |
| 62 | |
| 63 | accumulator[key][result[1]] = value; |
| 64 | }; |
| 65 | |
| 66 | case 'bracket': |
| 67 | return function (key, value, accumulator) { |
| 68 | result = /(\[\])$/.exec(key); |
| 69 | key = key.replace(/\[\]$/, ''); |
| 70 | |
| 71 | if (!result) { |
| 72 | accumulator[key] = value; |
| 73 | return; |
| 74 | } else if (accumulator[key] === undefined) { |
| 75 | accumulator[key] = [value]; |
| 76 | return; |
| 77 | } |
| 78 | |
| 79 | accumulator[key] = [].concat(accumulator[key], value); |
| 80 | }; |
| 81 | |
| 82 | default: |
| 83 | return function (key, value, accumulator) { |
| 84 | if (accumulator[key] === undefined) { |
| 85 | accumulator[key] = value; |
| 86 | return; |
| 87 | } |
| 88 | |
| 89 | accumulator[key] = [].concat(accumulator[key], value); |
| 90 | }; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | function encode(value, opts) { |
| 95 | if (opts.encode) { |
| 96 | return opts.strict ? strictUriEncode(value) : encodeURIComponent(value); |
| 97 | } |
| 98 | |
| 99 | return value; |
| 100 | } |
| 101 | |
| 102 | function keysSorter(input) { |
| 103 | if (Array.isArray(input)) { |
| 104 | return input.sort(); |
| 105 | } else if (typeof input === 'object') { |
| 106 | return keysSorter(Object.keys(input)).sort(function (a, b) { |
| 107 | return Number(a) - Number(b); |
| 108 | }).map(function (key) { |
| 109 | return input[key]; |
| 110 | }); |
| 111 | } |
| 112 | |
| 113 | return input; |
| 114 | } |
| 115 | |
| 116 | function extract(str) { |
| 117 | var queryStart = str.indexOf('?'); |
| 118 | if (queryStart === -1) { |
| 119 | return ''; |
| 120 | } |
| 121 | return str.slice(queryStart + 1); |
| 122 | } |
| 123 | |
| 124 | function parse(str, opts) { |
| 125 | opts = objectAssign({arrayFormat: 'none'}, opts); |
| 126 | |
| 127 | var formatter = parserForArrayFormat(opts); |
| 128 | |
| 129 | // Create an object with no prototype |
| 130 | // https://github.com/sindresorhus/query-string/issues/47 |
| 131 | var ret = Object.create(null); |
| 132 | |
| 133 | if (typeof str !== 'string') { |
| 134 | return ret; |
| 135 | } |
| 136 | |
| 137 | str = str.trim().replace(/^[?#&]/, ''); |
| 138 | |
| 139 | if (!str) { |
| 140 | return ret; |
| 141 | } |
| 142 | |
| 143 | str.split('&').forEach(function (param) { |
| 144 | var parts = param.replace(/\+/g, ' ').split('='); |
| 145 | // Firefox (pre 40) decodes `%3D` to `=` |
| 146 | // https://github.com/sindresorhus/query-string/pull/37 |
| 147 | var key = parts.shift(); |
| 148 | var val = parts.length > 0 ? parts.join('=') : undefined; |
| 149 | |
| 150 | // missing `=` should be `null`: |
| 151 | // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters |
| 152 | val = val === undefined ? null : decodeComponent(val); |
| 153 | |
| 154 | formatter(decodeComponent(key), val, ret); |
| 155 | }); |
| 156 | |
| 157 | return Object.keys(ret).sort().reduce(function (result, key) { |
| 158 | var val = ret[key]; |
| 159 | if (Boolean(val) && typeof val === 'object' && !Array.isArray(val)) { |
| 160 | // Sort object keys, not values |
| 161 | result[key] = keysSorter(val); |
| 162 | } else { |
| 163 | result[key] = val; |
| 164 | } |
| 165 | |
| 166 | return result; |
| 167 | }, Object.create(null)); |
| 168 | } |
| 169 | |
| 170 | exports.extract = extract; |
| 171 | exports.parse = parse; |
| 172 | |
| 173 | exports.stringify = function (obj, opts) { |
| 174 | var defaults = { |
| 175 | encode: true, |
| 176 | strict: true, |
| 177 | arrayFormat: 'none' |
| 178 | }; |
| 179 | |
| 180 | opts = objectAssign(defaults, opts); |
| 181 | |
| 182 | if (opts.sort === false) { |
| 183 | opts.sort = function () {}; |
| 184 | } |
| 185 | |
| 186 | var formatter = encoderForArrayFormat(opts); |
| 187 | |
| 188 | return obj ? Object.keys(obj).sort(opts.sort).map(function (key) { |
| 189 | var val = obj[key]; |
| 190 | |
| 191 | if (val === undefined) { |
| 192 | return ''; |
| 193 | } |
| 194 | |
| 195 | if (val === null) { |
| 196 | return encode(key, opts); |
| 197 | } |
| 198 | |
| 199 | if (Array.isArray(val)) { |
| 200 | var result = []; |
| 201 | |
| 202 | val.slice().forEach(function (val2) { |
| 203 | if (val2 === undefined) { |
| 204 | return; |
| 205 | } |
| 206 | |
| 207 | result.push(formatter(key, val2, result.length)); |
| 208 | }); |
| 209 | |
| 210 | return result.join('&'); |
| 211 | } |
| 212 | |
| 213 | return encode(key, opts) + '=' + encode(val, opts); |
| 214 | }).filter(function (x) { |
| 215 | return x.length > 0; |
| 216 | }).join('&') : ''; |
| 217 | }; |
| 218 | |
| 219 | exports.parseUrl = function (str, opts) { |
| 220 | return { |
| 221 | url: str.split('?')[0] || '', |
| 222 | query: parse(extract(str), opts) |
| 223 | }; |
| 224 | }; |