UNPKG

@rjsf/utils

Version:
85 lines 4.05 kB
import isObject from './isObject.js'; import { isValueEmpty, retrieveSchema } from './schema/index.js'; /** Recursively removes optional objects from the `formData` that are empty (i.e., all their fields * are undefined, null, empty strings, or themselves empty optional objects). This solves the problem * where interacting with fields inside an optional object "activates" it permanently, making the * form unsubmittable when the optional object has required inner fields. * * An object property is considered "optional" when it is NOT listed in its parent schema's `required` * array. * * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary * @param schema - The JSON schema describing the `formData` * @param [rootSchema] - The root schema, used primarily to look up `$ref`s * @param [formData] - The current form data to prune * @returns - A new copy of `formData` with empty optional objects removed, or `undefined` if the * entire formData was pruned * @deprecated - This function will be removed in a future release. The equivalent pruning behavior * is now built into `omitExtraData` — use that instead. */ // oxlint-disable-next-line @typescript-eslint/no-deprecated export default function removeOptionalEmptyObjects(validator, schema, rootSchema, formData) { if (!isObject(schema)) { return formData; } const resolvedSchema = retrieveSchema(validator, schema, rootSchema, formData); if (Array.isArray(formData)) { const itemsSchema = resolvedSchema.items; if (!itemsSchema) { return formData; } let hasChanges = false; const mapped = formData.map((item, index) => { let itemSchema = itemsSchema; if (Array.isArray(itemsSchema)) { itemSchema = itemsSchema[index] || resolvedSchema.additionalItems || {}; } // oxlint-disable-next-line @typescript-eslint/no-deprecated const cleaned = removeOptionalEmptyObjects(validator, itemSchema, rootSchema, item); if (cleaned !== item) { hasChanges = true; } return cleaned === undefined ? {} : cleaned; }); // Although T is an array type here, we still need to cast it back to T since TS // doesn't narrow the generic T automatically return hasChanges ? mapped : formData; } const { properties, required: requiredFields = [] } = resolvedSchema; if (!isObject(formData) || !properties) { return formData; } const result = {}; const data = formData; let hasAnyValue = false; for (const key of Object.keys(data)) { const value = data[key]; const propertySchema = (properties[key] || {}); const isRequired = requiredFields.includes(key); const isObj = isObject(value); const isArr = Array.isArray(value); if ((isObj || isArr) && properties[key]) { // Recursively process nested objects and arrays // oxlint-disable-next-line @typescript-eslint/no-deprecated const cleaned = removeOptionalEmptyObjects(validator, propertySchema, rootSchema, value); if (isRequired || !isValueEmpty(cleaned)) { result[key] = cleaned; hasAnyValue = true; } } else if (isRequired || !isValueEmpty(value) || !properties[key]) { // Keep: required, non-empty, or not schema-defined; skip optional empty scalars silently result[key] = value; if (!isValueEmpty(value)) { hasAnyValue = true; } } } // If the entire object ended up empty after pruning, return undefined so that the // caller (which may itself be a recursive call) can decide whether to keep or drop it if (!hasAnyValue && Object.keys(result).length === 0) { return undefined; } return result; } //# sourceMappingURL=removeOptionalEmptyObjects.js.map