'use strict'; var utils = require('../utils'); var AxiosError = require('../core/AxiosError'); var envFormData = require('../env/classes/FormData'); function isVisitable(thing) { return utils.isPlainObject(thing) || utils.isArray(thing); } function removeBrackets(key) { return utils.endsWith(key, '[]') ? key.slice(0, -2) : key; } function renderKey(path, key, dots) { if (!path) return key; return path.concat(key).map(function each(token, i) { // eslint-disable-next-line no-param-reassign token = removeBrackets(token); return !dots && i ? '[' + token + ']' : token; }).join(dots ? '.' : ''); } function isFlatArray(arr) { return utils.isArray(arr) && !arr.some(isVisitable); } var predicates = utils.toFlatObject(utils, {}, null, function filter(prop) { return /^is[A-Z]/.test(prop); }); function isSpecCompliant(thing) { return thing && utils.isFunction(thing.append) && thing[Symbol.toStringTag] === 'FormData' && thing[Symbol.iterator]; } /** * Convert a data object to FormData * @param {Object} obj * @param {?Object} [formData] * @param {?Object} [options] * @param {Function} [options.visitor] * @param {Boolean} [options.metaTokens = true] * @param {Boolean} [options.dots = false] * @param {?Boolean} [options.indexes = false] * @returns {Object} **/ function toFormData(obj, formData, options) { if (!utils.isObject(obj)) { throw new TypeError('target must be an object'); } // eslint-disable-next-line no-param-reassign formData = formData || new (envFormData || FormData)(); // eslint-disable-next-line no-param-reassign options = utils.toFlatObject(options, { metaTokens: true, dots: false, indexes: false }, false, function defined(option, source) { // eslint-disable-next-line no-eq-null,eqeqeq return !utils.isUndefined(source[option]); }); var metaTokens = options.metaTokens; // eslint-disable-next-line no-use-before-define var visitor = options.visitor || defaultVisitor; var dots = options.dots; var indexes = options.indexes; var _Blob = options.Blob || typeof Blob !== 'undefined' && Blob; var useBlob = _Blob && isSpecCompliant(formData); if (!utils.isFunction(visitor)) { throw new TypeError('visitor must be a function'); } function convertValue(value) { if (value === null) return ''; if (utils.isDate(value)) { return value.toISOString(); } if (!useBlob && utils.isBlob(value)) { throw new AxiosError('Blob is not supported. Use a Buffer instead.'); } if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) { return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value); } return value; } /** * * @param {*} value * @param {String|Number} key * @param {Array} path * @this {FormData} * @returns {boolean} return true to visit the each prop of the value recursively */ function defaultVisitor(value, key, path) { var arr = value; if (value && !path && typeof value === 'object') { if (utils.endsWith(key, '{}')) { // eslint-disable-next-line no-param-reassign key = metaTokens ? key : key.slice(0, -2); // eslint-disable-next-line no-param-reassign value = JSON.stringify(value); } else if ( (utils.isArray(value) && isFlatArray(value)) || (utils.isFileList(value) || utils.endsWith(key, '[]') && (arr = utils.toArray(value)) )) { // eslint-disable-next-line no-param-reassign key = removeBrackets(key); arr.forEach(function each(el, index) { !utils.isUndefined(el) && formData.append( // eslint-disable-next-line no-nested-ternary indexes === true ? renderKey([key], index, dots) : (indexes === null ? key : key + '[]'), convertValue(el) ); }); return false; } } if (isVisitable(value)) { return true; } formData.append(renderKey(path, key, dots), convertValue(value)); return false; } var stack = []; var exposedHelpers = Object.assign(predicates, { defaultVisitor: defaultVisitor, convertValue: convertValue, isVisitable: isVisitable }); function build(value, path) { if (utils.isUndefined(value)) return; if (stack.indexOf(value) !== -1) { throw Error('Circular reference detected in ' + path.join('.')); } stack.push(value); utils.forEach(value, function each(el, key) { var result = !utils.isUndefined(el) && visitor.call( formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers ); if (result === true) { build(el, path ? path.concat(key) : [key]); } }); stack.pop(); } if (!utils.isObject(obj)) { throw new TypeError('data must be an object'); } build(obj); return formData; } module.exports = toFormData;