UNPKG

@rjsf/utils

Version:
119 lines 5.88 kB
import { ADDITIONAL_PROPERTIES_KEY, ALL_OF_KEY, ANY_OF_KEY, ITEMS_KEY, ONE_OF_KEY, PROPERTIES_KEY, REF_KEY, RJSF_REF_KEY, } from './constants.js'; import findSchemaDefinition from './findSchemaDefinition.js'; import isObject from './isObject.js'; import mergeObjects from './mergeObjects.js'; // Keywords where child schemas map to uiSchema at the SAME key const SAME_KEY_KEYWORDS = [ITEMS_KEY, ADDITIONAL_PROPERTIES_KEY]; // Keywords where child schemas are in an array, each mapping to uiSchema[keyword][i] const ARRAY_KEYWORDS = [ONE_OF_KEY, ANY_OF_KEY, ALL_OF_KEY]; /** Expands `ui:definitions` into the uiSchema by walking the schema tree and finding all `$ref`s. * Called once at form initialization to pre-expand definitions into the uiSchema structure. * * For recursive schemas, expansion stops at recursion points to avoid infinite loops. * Runtime resolution via `resolveUiSchema` handles these cases using registry definitions. * * @param currentSchema - The current schema node being processed * @param uiSchema - The uiSchema at the current path * @param registry - The registry containing rootSchema and uiSchemaDefinitions * @param visited - Set of $refs already visited (to detect recursion) * @returns - The expanded uiSchema with definitions merged in */ export function expandUiSchemaDefinitions(currentSchema, uiSchema, registry, visited = new Set()) { const { rootSchema, uiSchemaDefinitions: definitions } = registry; let result = { ...uiSchema }; let resolvedSchema = currentSchema; const ref = currentSchema[REF_KEY]; const isRecursive = ref && visited.has(ref); if (ref) { visited.add(ref); if (definitions && ref in definitions) { result = mergeObjects(definitions[ref], result); } if (isRecursive) { return result; } try { resolvedSchema = findSchemaDefinition(ref, rootSchema); } catch (_a) { resolvedSchema = currentSchema; } } // Process properties (each property maps to uiSchema[propName] - flattened) const properties = resolvedSchema[PROPERTIES_KEY]; if (properties && isObject(properties)) { for (const [propName, propSchema] of Object.entries(properties)) { const propUiSchema = (result[propName] || {}); const expanded = expandUiSchemaDefinitions(propSchema, propUiSchema, registry, new Set(visited)); if (Object.keys(expanded).length > 0) { result[propName] = expanded; } } } // Process keywords where child maps to same key in uiSchema (items, additionalProperties) for (const keyword of SAME_KEY_KEYWORDS) { const subSchema = resolvedSchema[keyword]; if (subSchema && isObject(subSchema) && !Array.isArray(subSchema)) { const currentUiSchema = result[keyword]; if (typeof currentUiSchema !== 'function') { const subUiSchema = (currentUiSchema || {}); const expanded = expandUiSchemaDefinitions(subSchema, subUiSchema, registry, new Set(visited)); if (Object.keys(expanded).length > 0) { result[keyword] = expanded; } } } } // Process array keywords (oneOf, anyOf, allOf) - each option maps to uiSchema[keyword][i] for (const keyword of ARRAY_KEYWORDS) { const schemaOptions = resolvedSchema[keyword]; if (Array.isArray(schemaOptions) && schemaOptions.length > 0) { const currentUiSchemaArray = result[keyword]; const uiSchemaArray = Array.isArray(currentUiSchemaArray) ? [...currentUiSchemaArray] : []; let hasExpanded = false; for (let i = 0; i < schemaOptions.length; i++) { const optionSchema = schemaOptions[i]; const optionUiSchema = (uiSchemaArray[i] || {}); const expanded = expandUiSchemaDefinitions(optionSchema, optionUiSchema, registry, new Set(visited)); if (Object.keys(expanded).length > 0) { uiSchemaArray[i] = expanded; hasExpanded = true; } } if (hasExpanded) { result[keyword] = uiSchemaArray; } } } return result; } /** Resolves the uiSchema for a given schema, considering `ui:definitions` stored in the registry. * * This function is called at runtime for each field. It handles recursive schemas where the * pre-expansion in `expandUiSchemaDefinitions` couldn't go deeper. * * When the schema contains a `$ref`, this function looks up the corresponding uiSchema definition * from `registry.uiSchemaDefinitions` and merges it with any local uiSchema overrides. * * Resolution order (later sources override earlier): * 1. `ui:definitions[$ref]` - base definition from registry * 2. `localUiSchema` - local overrides at current path * * @param schema - The JSON schema (may still contain `$ref` for recursive schemas) * @param localUiSchema - The uiSchema at the current path (local overrides) * @param registry - The registry containing `uiSchemaDefinitions` * @returns - The resolved uiSchema with definitions merged in */ export default function resolveUiSchema(schema, localUiSchema, registry) { var _a, _b; const ref = ((_a = schema[RJSF_REF_KEY]) !== null && _a !== void 0 ? _a : schema[REF_KEY]); const definitionUiSchema = ref ? (_b = registry.uiSchemaDefinitions) === null || _b === void 0 ? void 0 : _b[ref] : undefined; if (!definitionUiSchema) { return localUiSchema || {}; } if (!localUiSchema || Object.keys(localUiSchema).length === 0) { return { ...definitionUiSchema }; } return mergeObjects(definitionUiSchema, localUiSchema); } //# sourceMappingURL=resolveUiSchema.js.map