repo_description: Promise based HTTP client for the browser and node.js
repo_topics: hacktoberfest http-client javascript nodejs promise
code: 'use strict';
  
  import utils from '../utils.js';
  
  /**
   * It takes a string like `foo[x][y][z]` and returns an array like `['foo', 'x', 'y', 'z']
   *
   * @param {string} name - The name of the property to get.
   *
   * @returns An array of strings.
   */
  function parsePropPath(name) {
    // foo[x][y][z]
    // foo.x.y.z
    // foo-x-y-z
    // foo x y z
    return utils.matchAll(/\w+|\[(\w*)]/g, name).map((match) => {
      return match[0] === '[]' ? '' : match[1] || match[0];
    });
  }
  
  /**
   * Convert an array to an object.
   *
   * @param {Array<any>} arr - The array to convert to an object.
   *
   * @returns An object with the same keys and values as the array.
   */
  function arrayToObject(arr) {
    const obj = {};
    const keys = Object.keys(arr);
    let i;
    const len = keys.length;
    let key;
    for (i = 0; i < len; i++) {
      key = keys[i];
      obj[key] = arr[key];
    }
    return obj;
  }
  
  /**
   * It takes a FormData object and returns a JavaScript object
   *
   * @param {string} formData The FormData object to convert to JSON.
   *
   * @returns {Object<string, any> | null} The converted object.
   */
  function formDataToJSON(formData) {
    function buildPath(path, value, target, index) {
      let name = path[index++];
  
      if (name === '__proto__') return true;
  
      const isNumericKey = Number.isFinite(+name);
      const isLast = index >= path.length;
      name = !name && utils.isArray(target) ? target.length : name;
  
      if (isLast) {
        if (utils.hasOwnProp(target, name)) {
          target[name] = utils.isArray(target[name])
            ? target[name].concat(value)
            : [target[name], value];
        } else {
          target[name] = value;
        }
  
        return !isNumericKey;
      }
  
      if (!target[name] || !utils.isObject(target[name])) {
        target[name] = [];
      }
  
      const result = buildPath(path, value, target[name], index);
  
      if (result && utils.isArray(target[name])) {
        target[name] = arrayToObject(target[name]);
      }
  
      return !isNumericKey;
    }
  
    if (utils.isFormData(formData) && utils.isFunction(formData.entries)) {
      const obj = {};
  
      utils.forEachEntry(formData, (name, value) => {
        buildPath(parsePropPath(name), value, obj, 0);
      });
  
      return obj;
    }
  
    return null;
  }
  
  export default formDataToJSON;
  