web.url-search-params.constructor.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. 'use strict';
  2. // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
  3. require('../modules/es.array.iterator');
  4. var $ = require('../internals/export');
  5. var global = require('../internals/global');
  6. var call = require('../internals/function-call');
  7. var uncurryThis = require('../internals/function-uncurry-this');
  8. var DESCRIPTORS = require('../internals/descriptors');
  9. var USE_NATIVE_URL = require('../internals/url-constructor-detection');
  10. var defineBuiltIn = require('../internals/define-built-in');
  11. var defineBuiltIns = require('../internals/define-built-ins');
  12. var setToStringTag = require('../internals/set-to-string-tag');
  13. var createIteratorConstructor = require('../internals/iterator-create-constructor');
  14. var InternalStateModule = require('../internals/internal-state');
  15. var anInstance = require('../internals/an-instance');
  16. var isCallable = require('../internals/is-callable');
  17. var hasOwn = require('../internals/has-own-property');
  18. var bind = require('../internals/function-bind-context');
  19. var classof = require('../internals/classof');
  20. var anObject = require('../internals/an-object');
  21. var isObject = require('../internals/is-object');
  22. var $toString = require('../internals/to-string');
  23. var create = require('../internals/object-create');
  24. var createPropertyDescriptor = require('../internals/create-property-descriptor');
  25. var getIterator = require('../internals/get-iterator');
  26. var getIteratorMethod = require('../internals/get-iterator-method');
  27. var validateArgumentsLength = require('../internals/validate-arguments-length');
  28. var wellKnownSymbol = require('../internals/well-known-symbol');
  29. var arraySort = require('../internals/array-sort');
  30. var ITERATOR = wellKnownSymbol('iterator');
  31. var URL_SEARCH_PARAMS = 'URLSearchParams';
  32. var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
  33. var setInternalState = InternalStateModule.set;
  34. var getInternalParamsState = InternalStateModule.getterFor(URL_SEARCH_PARAMS);
  35. var getInternalIteratorState = InternalStateModule.getterFor(URL_SEARCH_PARAMS_ITERATOR);
  36. // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
  37. var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
  38. // Avoid NodeJS experimental warning
  39. var safeGetBuiltIn = function (name) {
  40. if (!DESCRIPTORS) return global[name];
  41. var descriptor = getOwnPropertyDescriptor(global, name);
  42. return descriptor && descriptor.value;
  43. };
  44. var nativeFetch = safeGetBuiltIn('fetch');
  45. var NativeRequest = safeGetBuiltIn('Request');
  46. var Headers = safeGetBuiltIn('Headers');
  47. var RequestPrototype = NativeRequest && NativeRequest.prototype;
  48. var HeadersPrototype = Headers && Headers.prototype;
  49. var RegExp = global.RegExp;
  50. var TypeError = global.TypeError;
  51. var decodeURIComponent = global.decodeURIComponent;
  52. var encodeURIComponent = global.encodeURIComponent;
  53. var charAt = uncurryThis(''.charAt);
  54. var join = uncurryThis([].join);
  55. var push = uncurryThis([].push);
  56. var replace = uncurryThis(''.replace);
  57. var shift = uncurryThis([].shift);
  58. var splice = uncurryThis([].splice);
  59. var split = uncurryThis(''.split);
  60. var stringSlice = uncurryThis(''.slice);
  61. var plus = /\+/g;
  62. var sequences = Array(4);
  63. var percentSequence = function (bytes) {
  64. return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
  65. };
  66. var percentDecode = function (sequence) {
  67. try {
  68. return decodeURIComponent(sequence);
  69. } catch (error) {
  70. return sequence;
  71. }
  72. };
  73. var deserialize = function (it) {
  74. var result = replace(it, plus, ' ');
  75. var bytes = 4;
  76. try {
  77. return decodeURIComponent(result);
  78. } catch (error) {
  79. while (bytes) {
  80. result = replace(result, percentSequence(bytes--), percentDecode);
  81. }
  82. return result;
  83. }
  84. };
  85. var find = /[!'()~]|%20/g;
  86. var replacements = {
  87. '!': '%21',
  88. "'": '%27',
  89. '(': '%28',
  90. ')': '%29',
  91. '~': '%7E',
  92. '%20': '+'
  93. };
  94. var replacer = function (match) {
  95. return replacements[match];
  96. };
  97. var serialize = function (it) {
  98. return replace(encodeURIComponent(it), find, replacer);
  99. };
  100. var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
  101. setInternalState(this, {
  102. type: URL_SEARCH_PARAMS_ITERATOR,
  103. iterator: getIterator(getInternalParamsState(params).entries),
  104. kind: kind
  105. });
  106. }, 'Iterator', function next() {
  107. var state = getInternalIteratorState(this);
  108. var kind = state.kind;
  109. var step = state.iterator.next();
  110. var entry = step.value;
  111. if (!step.done) {
  112. step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
  113. } return step;
  114. }, true);
  115. var URLSearchParamsState = function (init) {
  116. this.entries = [];
  117. this.url = null;
  118. if (init !== undefined) {
  119. if (isObject(init)) this.parseObject(init);
  120. else this.parseQuery(typeof init == 'string' ? charAt(init, 0) === '?' ? stringSlice(init, 1) : init : $toString(init));
  121. }
  122. };
  123. URLSearchParamsState.prototype = {
  124. type: URL_SEARCH_PARAMS,
  125. bindURL: function (url) {
  126. this.url = url;
  127. this.update();
  128. },
  129. parseObject: function (object) {
  130. var iteratorMethod = getIteratorMethod(object);
  131. var iterator, next, step, entryIterator, entryNext, first, second;
  132. if (iteratorMethod) {
  133. iterator = getIterator(object, iteratorMethod);
  134. next = iterator.next;
  135. while (!(step = call(next, iterator)).done) {
  136. entryIterator = getIterator(anObject(step.value));
  137. entryNext = entryIterator.next;
  138. if (
  139. (first = call(entryNext, entryIterator)).done ||
  140. (second = call(entryNext, entryIterator)).done ||
  141. !call(entryNext, entryIterator).done
  142. ) throw TypeError('Expected sequence with length 2');
  143. push(this.entries, { key: $toString(first.value), value: $toString(second.value) });
  144. }
  145. } else for (var key in object) if (hasOwn(object, key)) {
  146. push(this.entries, { key: key, value: $toString(object[key]) });
  147. }
  148. },
  149. parseQuery: function (query) {
  150. if (query) {
  151. var attributes = split(query, '&');
  152. var index = 0;
  153. var attribute, entry;
  154. while (index < attributes.length) {
  155. attribute = attributes[index++];
  156. if (attribute.length) {
  157. entry = split(attribute, '=');
  158. push(this.entries, {
  159. key: deserialize(shift(entry)),
  160. value: deserialize(join(entry, '='))
  161. });
  162. }
  163. }
  164. }
  165. },
  166. serialize: function () {
  167. var entries = this.entries;
  168. var result = [];
  169. var index = 0;
  170. var entry;
  171. while (index < entries.length) {
  172. entry = entries[index++];
  173. push(result, serialize(entry.key) + '=' + serialize(entry.value));
  174. } return join(result, '&');
  175. },
  176. update: function () {
  177. this.entries.length = 0;
  178. this.parseQuery(this.url.query);
  179. },
  180. updateURL: function () {
  181. if (this.url) this.url.update();
  182. }
  183. };
  184. // `URLSearchParams` constructor
  185. // https://url.spec.whatwg.org/#interface-urlsearchparams
  186. var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
  187. anInstance(this, URLSearchParamsPrototype);
  188. var init = arguments.length > 0 ? arguments[0] : undefined;
  189. setInternalState(this, new URLSearchParamsState(init));
  190. };
  191. var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
  192. defineBuiltIns(URLSearchParamsPrototype, {
  193. // `URLSearchParams.prototype.append` method
  194. // https://url.spec.whatwg.org/#dom-urlsearchparams-append
  195. append: function append(name, value) {
  196. validateArgumentsLength(arguments.length, 2);
  197. var state = getInternalParamsState(this);
  198. push(state.entries, { key: $toString(name), value: $toString(value) });
  199. state.updateURL();
  200. },
  201. // `URLSearchParams.prototype.delete` method
  202. // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
  203. 'delete': function (name) {
  204. validateArgumentsLength(arguments.length, 1);
  205. var state = getInternalParamsState(this);
  206. var entries = state.entries;
  207. var key = $toString(name);
  208. var index = 0;
  209. while (index < entries.length) {
  210. if (entries[index].key === key) splice(entries, index, 1);
  211. else index++;
  212. }
  213. state.updateURL();
  214. },
  215. // `URLSearchParams.prototype.get` method
  216. // https://url.spec.whatwg.org/#dom-urlsearchparams-get
  217. get: function get(name) {
  218. validateArgumentsLength(arguments.length, 1);
  219. var entries = getInternalParamsState(this).entries;
  220. var key = $toString(name);
  221. var index = 0;
  222. for (; index < entries.length; index++) {
  223. if (entries[index].key === key) return entries[index].value;
  224. }
  225. return null;
  226. },
  227. // `URLSearchParams.prototype.getAll` method
  228. // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
  229. getAll: function getAll(name) {
  230. validateArgumentsLength(arguments.length, 1);
  231. var entries = getInternalParamsState(this).entries;
  232. var key = $toString(name);
  233. var result = [];
  234. var index = 0;
  235. for (; index < entries.length; index++) {
  236. if (entries[index].key === key) push(result, entries[index].value);
  237. }
  238. return result;
  239. },
  240. // `URLSearchParams.prototype.has` method
  241. // https://url.spec.whatwg.org/#dom-urlsearchparams-has
  242. has: function has(name) {
  243. validateArgumentsLength(arguments.length, 1);
  244. var entries = getInternalParamsState(this).entries;
  245. var key = $toString(name);
  246. var index = 0;
  247. while (index < entries.length) {
  248. if (entries[index++].key === key) return true;
  249. }
  250. return false;
  251. },
  252. // `URLSearchParams.prototype.set` method
  253. // https://url.spec.whatwg.org/#dom-urlsearchparams-set
  254. set: function set(name, value) {
  255. validateArgumentsLength(arguments.length, 1);
  256. var state = getInternalParamsState(this);
  257. var entries = state.entries;
  258. var found = false;
  259. var key = $toString(name);
  260. var val = $toString(value);
  261. var index = 0;
  262. var entry;
  263. for (; index < entries.length; index++) {
  264. entry = entries[index];
  265. if (entry.key === key) {
  266. if (found) splice(entries, index--, 1);
  267. else {
  268. found = true;
  269. entry.value = val;
  270. }
  271. }
  272. }
  273. if (!found) push(entries, { key: key, value: val });
  274. state.updateURL();
  275. },
  276. // `URLSearchParams.prototype.sort` method
  277. // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
  278. sort: function sort() {
  279. var state = getInternalParamsState(this);
  280. arraySort(state.entries, function (a, b) {
  281. return a.key > b.key ? 1 : -1;
  282. });
  283. state.updateURL();
  284. },
  285. // `URLSearchParams.prototype.forEach` method
  286. forEach: function forEach(callback /* , thisArg */) {
  287. var entries = getInternalParamsState(this).entries;
  288. var boundFunction = bind(callback, arguments.length > 1 ? arguments[1] : undefined);
  289. var index = 0;
  290. var entry;
  291. while (index < entries.length) {
  292. entry = entries[index++];
  293. boundFunction(entry.value, entry.key, this);
  294. }
  295. },
  296. // `URLSearchParams.prototype.keys` method
  297. keys: function keys() {
  298. return new URLSearchParamsIterator(this, 'keys');
  299. },
  300. // `URLSearchParams.prototype.values` method
  301. values: function values() {
  302. return new URLSearchParamsIterator(this, 'values');
  303. },
  304. // `URLSearchParams.prototype.entries` method
  305. entries: function entries() {
  306. return new URLSearchParamsIterator(this, 'entries');
  307. }
  308. }, { enumerable: true });
  309. // `URLSearchParams.prototype[@@iterator]` method
  310. defineBuiltIn(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries, { name: 'entries' });
  311. // `URLSearchParams.prototype.toString` method
  312. // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
  313. defineBuiltIn(URLSearchParamsPrototype, 'toString', function toString() {
  314. return getInternalParamsState(this).serialize();
  315. }, { enumerable: true });
  316. setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
  317. $({ global: true, constructor: true, forced: !USE_NATIVE_URL }, {
  318. URLSearchParams: URLSearchParamsConstructor
  319. });
  320. // Wrap `fetch` and `Request` for correct work with polyfilled `URLSearchParams`
  321. if (!USE_NATIVE_URL && isCallable(Headers)) {
  322. var headersHas = uncurryThis(HeadersPrototype.has);
  323. var headersSet = uncurryThis(HeadersPrototype.set);
  324. var wrapRequestOptions = function (init) {
  325. if (isObject(init)) {
  326. var body = init.body;
  327. var headers;
  328. if (classof(body) === URL_SEARCH_PARAMS) {
  329. headers = init.headers ? new Headers(init.headers) : new Headers();
  330. if (!headersHas(headers, 'content-type')) {
  331. headersSet(headers, 'content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
  332. }
  333. return create(init, {
  334. body: createPropertyDescriptor(0, $toString(body)),
  335. headers: createPropertyDescriptor(0, headers)
  336. });
  337. }
  338. } return init;
  339. };
  340. if (isCallable(nativeFetch)) {
  341. $({ global: true, enumerable: true, dontCallGetSet: true, forced: true }, {
  342. fetch: function fetch(input /* , init */) {
  343. return nativeFetch(input, arguments.length > 1 ? wrapRequestOptions(arguments[1]) : {});
  344. }
  345. });
  346. }
  347. if (isCallable(NativeRequest)) {
  348. var RequestConstructor = function Request(input /* , init */) {
  349. anInstance(this, RequestPrototype);
  350. return new NativeRequest(input, arguments.length > 1 ? wrapRequestOptions(arguments[1]) : {});
  351. };
  352. RequestPrototype.constructor = RequestConstructor;
  353. RequestConstructor.prototype = RequestPrototype;
  354. $({ global: true, constructor: true, dontCallGetSet: true, forced: true }, {
  355. Request: RequestConstructor
  356. });
  357. }
  358. }
  359. module.exports = {
  360. URLSearchParams: URLSearchParamsConstructor,
  361. getState: getInternalParamsState
  362. };