1f6ff7000f8f6e572b560239f4f821b324a72e96b9beff7a749020601de0248da3293cd2467a433b62f245f619d22d2d4b59462d279cb392c7e772734c9533 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. 'use strict';
  2. var utils = require('../utils');
  3. var AxiosError = require('../core/AxiosError');
  4. var envFormData = require('../env/classes/FormData');
  5. function isVisitable(thing) {
  6. return utils.isPlainObject(thing) || utils.isArray(thing);
  7. }
  8. function removeBrackets(key) {
  9. return utils.endsWith(key, '[]') ? key.slice(0, -2) : key;
  10. }
  11. function renderKey(path, key, dots) {
  12. if (!path) return key;
  13. return path.concat(key).map(function each(token, i) {
  14. // eslint-disable-next-line no-param-reassign
  15. token = removeBrackets(token);
  16. return !dots && i ? '[' + token + ']' : token;
  17. }).join(dots ? '.' : '');
  18. }
  19. function isFlatArray(arr) {
  20. return utils.isArray(arr) && !arr.some(isVisitable);
  21. }
  22. var predicates = utils.toFlatObject(utils, {}, null, function filter(prop) {
  23. return /^is[A-Z]/.test(prop);
  24. });
  25. function isSpecCompliant(thing) {
  26. return thing && utils.isFunction(thing.append) && thing[Symbol.toStringTag] === 'FormData' && thing[Symbol.iterator];
  27. }
  28. /**
  29. * Convert a data object to FormData
  30. * @param {Object} obj
  31. * @param {?Object} [formData]
  32. * @param {?Object} [options]
  33. * @param {Function} [options.visitor]
  34. * @param {Boolean} [options.metaTokens = true]
  35. * @param {Boolean} [options.dots = false]
  36. * @param {?Boolean} [options.indexes = false]
  37. * @returns {Object}
  38. **/
  39. function toFormData(obj, formData, options) {
  40. if (!utils.isObject(obj)) {
  41. throw new TypeError('target must be an object');
  42. }
  43. // eslint-disable-next-line no-param-reassign
  44. formData = formData || new (envFormData || FormData)();
  45. // eslint-disable-next-line no-param-reassign
  46. options = utils.toFlatObject(options, {
  47. metaTokens: true,
  48. dots: false,
  49. indexes: false
  50. }, false, function defined(option, source) {
  51. // eslint-disable-next-line no-eq-null,eqeqeq
  52. return !utils.isUndefined(source[option]);
  53. });
  54. var metaTokens = options.metaTokens;
  55. // eslint-disable-next-line no-use-before-define
  56. var visitor = options.visitor || defaultVisitor;
  57. var dots = options.dots;
  58. var indexes = options.indexes;
  59. var _Blob = options.Blob || typeof Blob !== 'undefined' && Blob;
  60. var useBlob = _Blob && isSpecCompliant(formData);
  61. if (!utils.isFunction(visitor)) {
  62. throw new TypeError('visitor must be a function');
  63. }
  64. function convertValue(value) {
  65. if (value === null) return '';
  66. if (utils.isDate(value)) {
  67. return value.toISOString();
  68. }
  69. if (!useBlob && utils.isBlob(value)) {
  70. throw new AxiosError('Blob is not supported. Use a Buffer instead.');
  71. }
  72. if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
  73. return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
  74. }
  75. return value;
  76. }
  77. /**
  78. *
  79. * @param {*} value
  80. * @param {String|Number} key
  81. * @param {Array<String|Number>} path
  82. * @this {FormData}
  83. * @returns {boolean} return true to visit the each prop of the value recursively
  84. */
  85. function defaultVisitor(value, key, path) {
  86. var arr = value;
  87. if (value && !path && typeof value === 'object') {
  88. if (utils.endsWith(key, '{}')) {
  89. // eslint-disable-next-line no-param-reassign
  90. key = metaTokens ? key : key.slice(0, -2);
  91. // eslint-disable-next-line no-param-reassign
  92. value = JSON.stringify(value);
  93. } else if (
  94. (utils.isArray(value) && isFlatArray(value)) ||
  95. (utils.isFileList(value) || utils.endsWith(key, '[]') && (arr = utils.toArray(value))
  96. )) {
  97. // eslint-disable-next-line no-param-reassign
  98. key = removeBrackets(key);
  99. arr.forEach(function each(el, index) {
  100. !utils.isUndefined(el) && formData.append(
  101. // eslint-disable-next-line no-nested-ternary
  102. indexes === true ? renderKey([key], index, dots) : (indexes === null ? key : key + '[]'),
  103. convertValue(el)
  104. );
  105. });
  106. return false;
  107. }
  108. }
  109. if (isVisitable(value)) {
  110. return true;
  111. }
  112. formData.append(renderKey(path, key, dots), convertValue(value));
  113. return false;
  114. }
  115. var stack = [];
  116. var exposedHelpers = Object.assign(predicates, {
  117. defaultVisitor: defaultVisitor,
  118. convertValue: convertValue,
  119. isVisitable: isVisitable
  120. });
  121. function build(value, path) {
  122. if (utils.isUndefined(value)) return;
  123. if (stack.indexOf(value) !== -1) {
  124. throw Error('Circular reference detected in ' + path.join('.'));
  125. }
  126. stack.push(value);
  127. utils.forEach(value, function each(el, key) {
  128. var result = !utils.isUndefined(el) && visitor.call(
  129. formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers
  130. );
  131. if (result === true) {
  132. build(el, path ? path.concat(key) : [key]);
  133. }
  134. });
  135. stack.pop();
  136. }
  137. if (!utils.isObject(obj)) {
  138. throw new TypeError('data must be an object');
  139. }
  140. build(obj);
  141. return formData;
  142. }
  143. module.exports = toFormData;