index.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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 || accumulator[key] === undefined) {
  60. accumulator[key] = [value];
  61. return;
  62. }
  63. accumulator[key] = [].concat(accumulator[key], value);
  64. };
  65. default:
  66. return function (key, value, accumulator) {
  67. if (accumulator[key] === undefined) {
  68. accumulator[key] = value;
  69. return;
  70. }
  71. accumulator[key] = [].concat(accumulator[key], value);
  72. };
  73. }
  74. }
  75. function encode(value, opts) {
  76. if (opts.encode) {
  77. return opts.strict ? strictUriEncode(value) : encodeURIComponent(value);
  78. }
  79. return value;
  80. }
  81. function keysSorter(input) {
  82. if (Array.isArray(input)) {
  83. return input.sort();
  84. } else if (typeof input === 'object') {
  85. return keysSorter(Object.keys(input)).sort(function (a, b) {
  86. return Number(a) - Number(b);
  87. }).map(function (key) {
  88. return input[key];
  89. });
  90. }
  91. return input;
  92. }
  93. exports.extract = function (str) {
  94. return str.split('?')[1] || '';
  95. };
  96. exports.parse = function (str, opts) {
  97. opts = objectAssign({arrayFormat: 'none'}, opts);
  98. var formatter = parserForArrayFormat(opts);
  99. // Create an object with no prototype
  100. // https://github.com/sindresorhus/query-string/issues/47
  101. var ret = Object.create(null);
  102. if (typeof str !== 'string') {
  103. return ret;
  104. }
  105. str = str.trim().replace(/^(\?|#|&)/, '');
  106. if (!str) {
  107. return ret;
  108. }
  109. str.split('&').forEach(function (param) {
  110. var parts = param.replace(/\+/g, ' ').split('=');
  111. // Firefox (pre 40) decodes `%3D` to `=`
  112. // https://github.com/sindresorhus/query-string/pull/37
  113. var key = parts.shift();
  114. var val = parts.length > 0 ? parts.join('=') : undefined;
  115. // missing `=` should be `null`:
  116. // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
  117. val = val === undefined ? null : decodeURIComponent(val);
  118. formatter(decodeURIComponent(key), val, ret);
  119. });
  120. return Object.keys(ret).sort().reduce(function (result, key) {
  121. var val = ret[key];
  122. if (Boolean(val) && typeof val === 'object' && !Array.isArray(val)) {
  123. // Sort object keys, not values
  124. result[key] = keysSorter(val);
  125. } else {
  126. result[key] = val;
  127. }
  128. return result;
  129. }, Object.create(null));
  130. };
  131. exports.stringify = function (obj, opts) {
  132. var defaults = {
  133. encode: true,
  134. strict: true,
  135. arrayFormat: 'none'
  136. };
  137. opts = objectAssign(defaults, opts);
  138. var formatter = encoderForArrayFormat(opts);
  139. return obj ? Object.keys(obj).sort().map(function (key) {
  140. var val = obj[key];
  141. if (val === undefined) {
  142. return '';
  143. }
  144. if (val === null) {
  145. return encode(key, opts);
  146. }
  147. if (Array.isArray(val)) {
  148. var result = [];
  149. val.slice().forEach(function (val2) {
  150. if (val2 === undefined) {
  151. return;
  152. }
  153. result.push(formatter(key, val2, result.length));
  154. });
  155. return result.join('&');
  156. }
  157. return encode(key, opts) + '=' + encode(val, opts);
  158. }).filter(function (x) {
  159. return x.length > 0;
  160. }).join('&') : '';
  161. };