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

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