UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

253 lines (252 loc) 10.2 kB
import { isDateLike } from '../date/guards.js'; import { isEmptyObject, isNotEmptyObject, isValidArray } from '../guards/non-primitives.js'; import { isNonEmptyString, isString } from '../guards/primitives.js'; import { trimString } from '../string/basics.js'; import { isCustomFile, isCustomFileArray, isFileArray, isFileList, isFileOrBlob, isFileUpload, } from './guards.js'; export const createControlledFormData = (data, configs) => { if (typeof FormData === 'undefined') { throw new Error('FormData is not available! Please make sure your environment supports FormData.'); } const formData = new FormData(); const { stringifyNested = '*', ignoreKeys = [], breakArray, lowerCaseKeys, dotNotateNested, lowerCaseValues, requiredKeys, } = configs || {}; const _compareKeyPaths = (key, paths) => { return paths.some((path) => key === path || key.startsWith(`${path}.`)); }; const _shouldLowercaseKeys = (key) => { return Array.isArray(lowerCaseKeys) ? _compareKeyPaths(key, lowerCaseKeys) : lowerCaseKeys === '*'; }; const _shouldLowercaseValue = (key) => { return Array.isArray(lowerCaseValues) ? _compareKeyPaths(key, lowerCaseValues) : lowerCaseValues === '*'; }; const _transformKey = (key) => { return (_shouldLowercaseKeys(key) ? key.toLowerCase() : key); }; const _parseDateLike = (value) => { return String(JSON.parse(JSON.stringify(value))); }; const _isRequiredKey = (key) => { const transformedKey = _transformKey(key); return Array.isArray(requiredKeys) ? _compareKeyPaths(transformedKey, requiredKeys) : requiredKeys === '*'; }; const _shouldDotNotate = (key) => { const transformedKey = _transformKey(key); return Array.isArray(dotNotateNested) ? _compareKeyPaths(transformedKey, dotNotateNested) : dotNotateNested === '*'; }; const _shouldStringify = (key) => { const transformedKey = _transformKey(key); return Array.isArray(stringifyNested) ? _compareKeyPaths(transformedKey, stringifyNested) : stringifyNested === '*'; }; const _shouldBreakArray = (key) => { const transformedKey = _transformKey(key); return Array.isArray(breakArray) ? _compareKeyPaths(transformedKey, breakArray) : breakArray === '*'; }; const _cleanObject = (obj, parentKey = '') => { return Object.entries(obj).reduce((acc, [key, value]) => { const transformedKey = _transformKey(key); const fullKey = parentKey ? `${parentKey}.${transformedKey}` : transformedKey; if (_compareKeyPaths(fullKey, ignoreKeys)) { return acc; } const shouldKeep = (value != null && value !== '') || _isRequiredKey(fullKey) || isNonEmptyString(value) || isValidArray(value) || isNotEmptyObject(value); if (shouldKeep) { if (isDateLike(value)) { acc[transformedKey] = value; } else if (isNotEmptyObject(value)) { if (isDateLike(value)) { acc[transformedKey] = value; } else { const cleaned = _cleanObject(value, fullKey); if (_isRequiredKey(fullKey) || isNotEmptyObject(cleaned)) { acc[transformedKey] = cleaned; } } } else { if (isString(value)) { if (isNonEmptyString(value)) { let cleanString = value; if (configs?.trimStrings) { cleanString = trimString(cleanString); } if (_shouldLowercaseValue(fullKey)) { cleanString = cleanString?.toLowerCase(); } acc[transformedKey] = cleanString; } else { acc[transformedKey] = value; } } else if (Array.isArray(value)) { if (_isRequiredKey(fullKey) || isValidArray(value)) { acc[transformedKey] = value; } } else if (isEmptyObject(value)) { if (_isRequiredKey(fullKey)) { acc[transformedKey] = value; } } else { acc[transformedKey] = value; } } } return acc; }, {}); }; const _addToFormData = (key, value) => { const transformedKey = _transformKey(key); if (_compareKeyPaths(transformedKey, ignoreKeys)) { return; } if (isCustomFileArray(value)) { for (const file of value) { formData.append(transformedKey, file?.originFileObj); } } else if (isFileUpload(value)) { if (value?.fileList) { for (const file of value.fileList) { formData.append(transformedKey, file?.originFileObj); } } else if (value?.file) { if (isCustomFile(value?.file)) { formData.append(transformedKey, value?.file?.originFileObj); } else { formData.append(transformedKey, value?.file); } } } else if (isFileOrBlob(value)) { formData.append(transformedKey, value); } else if (isFileList(value)) { for (let i = 0; i < value?.length; i++) { formData.append(transformedKey, value.item(i)); } } else if (Array.isArray(value)) { if (isFileArray(value)) { if (_shouldBreakArray(key)) { value?.forEach((item, index) => { _addToFormData(`${transformedKey}[${index}]`, item); }); } else { value?.forEach((item) => { formData.append(transformedKey, item); }); } } else if (isValidArray(value)) { if (_shouldBreakArray(key)) { value?.forEach((item, index) => { _addToFormData(`${transformedKey}[${index}]`, item); }); } else { formData.append(transformedKey, JSON.stringify(value)); } } else if (_isRequiredKey(key)) { formData.append(transformedKey, JSON.stringify(value)); } } else if (isDateLike(value)) { formData.append(transformedKey, _parseDateLike(value)); } else if (isNotEmptyObject(value)) { if (_shouldStringify(key) && !_shouldDotNotate(key)) { const cleanedValue = _cleanObject(value, key); if (isNotEmptyObject(cleanedValue) || _isRequiredKey(key)) { formData.append(transformedKey, JSON.stringify(cleanedValue)); } } else { Object.entries(value).forEach(([nestedKey, nestedValue]) => { _addToFormData(`${key}.${nestedKey}`, nestedValue); }); } } else { const isNotNullish = value != null && value !== ''; if (isNotNullish || _isRequiredKey(key)) { if (isString(value)) { let processedValue = value; if (configs?.trimStrings) { processedValue = trimString(processedValue); } if (_shouldLowercaseValue(key)) { processedValue = processedValue.toLowerCase(); } formData.append(transformedKey, processedValue); } else { formData.append(transformedKey, value); } } } }; const _processObject = (obj, parentKey = '') => { Object.entries(obj).forEach(([key, value]) => { const transformedKey = _transformKey(key); const fullKey = parentKey ? `${parentKey}.${transformedKey}` : transformedKey; if (_compareKeyPaths(fullKey, ignoreKeys)) { return; } if (configs?.trimStrings && isNonEmptyString(value)) { value = trimString(value); } if (_shouldDotNotate(fullKey)) { _addToFormData(fullKey, value); } else if (isNotEmptyObject(value) && !_shouldStringify(fullKey)) { if (isDateLike(value)) { _addToFormData(key, _parseDateLike(value)); } else { _processObject(value, key); } } else if (isFileOrBlob(value)) { _addToFormData(key, value); } else if (isDateLike(value)) { _addToFormData(key, _parseDateLike(value)); } else if (isEmptyObject(value)) { if (_isRequiredKey(fullKey)) { _addToFormData(key, JSON.stringify(value)); } } else { _addToFormData(key, value); } }); }; if (isNotEmptyObject(data)) { _processObject(data); } return formData; };