index.js 4.2 KB

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