You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 4.5KB

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