@rjsf/utils 6.2.5 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/types.d.ts CHANGED
@@ -413,6 +413,10 @@ export interface Registry<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
413
413
  readonly globalFormOptions: GlobalFormOptions;
414
414
  /** The optional global UI Options that are available for all templates, fields and widgets to access */
415
415
  globalUiOptions?: GlobalUISchemaOptions;
416
+ /** The optional uiSchema definitions extracted from the root uiSchema, keyed by `$ref` paths.
417
+ * Used to automatically apply uiSchema when a schema with a matching `$ref` is resolved.
418
+ */
419
+ uiSchemaDefinitions?: UiSchemaDefinitions<T, S, F>;
416
420
  }
417
421
  /** The properties that are passed to a `Field` implementation */
418
422
  export interface FieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, RJSFBaseProps<T, S, F>, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
@@ -892,6 +896,14 @@ export type UIOptionsType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
892
896
  * Handles both standard arrays and readonly arrays.
893
897
  */
894
898
  export type ArrayElement<A> = A extends readonly (infer E)[] ? E : A;
899
+ /** Type describing the uiSchema definitions that can be applied to schemas referenced by `$ref`.
900
+ * Keys are the full `$ref` path (e.g., '#/$defs/node', '#/definitions/address').
901
+ * When a schema with a matching `$ref` is resolved, the corresponding uiSchema definition
902
+ * is automatically applied and merged with any local uiSchema overrides.
903
+ */
904
+ export type UiSchemaDefinitions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = {
905
+ [refPath: string]: UiSchema<T, S, F>;
906
+ };
895
907
  /** Type describing the well-known properties of the `UiSchema` while also supporting all user defined properties,
896
908
  * starting with `ui:`.
897
909
  */
@@ -920,6 +932,12 @@ export type UiSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
920
932
  * When using a function, it receives the item data, index, and optionally the form context as parameters.
921
933
  */
922
934
  items?: UiSchema<ArrayElement<T>, S, F> | ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
935
+ /** An object containing uiSchema definitions keyed by JSON Schema `$ref` paths.
936
+ * When a schema with a `$ref` is resolved, the corresponding uiSchema definition is automatically
937
+ * applied and merged with any local uiSchema overrides at that path.
938
+ * Keys must be full `$ref` paths (e.g., '#/$defs/node', '#/definitions/address').
939
+ */
940
+ 'ui:definitions'?: UiSchemaDefinitions<T, S, F>;
923
941
  };
924
942
  /** A `CustomValidator` function takes in a `formData`, `errors`, `uiSchema` and `errorSchema` objects and returns the given `errors`
925
943
  * object back, while potentially adding additional messages to the `errors`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rjsf/utils",
3
- "version": "6.2.5",
3
+ "version": "6.3.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -68,8 +68,8 @@
68
68
  "@x0k/json-schema-merge": "^1.0.2",
69
69
  "fast-uri": "^3.1.0",
70
70
  "jsonpointer": "^5.0.1",
71
- "lodash": "^4.17.21",
72
- "lodash-es": "^4.17.21",
71
+ "lodash": "^4.17.23",
72
+ "lodash-es": "^4.17.23",
73
73
  "react-is": "^18.3.1"
74
74
  },
75
75
  "devDependencies": {
package/src/constants.ts CHANGED
@@ -45,6 +45,7 @@ export const UI_FIELD_KEY = 'ui:field';
45
45
  export const UI_WIDGET_KEY = 'ui:widget';
46
46
  export const UI_OPTIONS_KEY = 'ui:options';
47
47
  export const UI_GLOBAL_OPTIONS_KEY = 'ui:globalOptions';
48
+ export const UI_DEFINITIONS_KEY = 'ui:definitions';
48
49
 
49
50
  /** The JSON Schema version strings
50
51
  */
@@ -38,9 +38,11 @@ import get from 'lodash/get';
38
38
  * providing a simplified set of APIs to the `@rjsf/core` components and the various themes as well. This class
39
39
  * implements the `SchemaUtilsType` interface.
40
40
  */
41
- class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
42
- implements SchemaUtilsType<T, S, F>
43
- {
41
+ class SchemaUtils<
42
+ T = any,
43
+ S extends StrictRJSFSchema = RJSFSchema,
44
+ F extends FormContextType = any,
45
+ > implements SchemaUtilsType<T, S, F> {
44
46
  rootSchema: S;
45
47
  validator: ValidatorType<T, S, F>;
46
48
  experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior;
package/src/index.ts CHANGED
@@ -58,6 +58,7 @@ import pad from './pad';
58
58
  import parseDateString from './parseDateString';
59
59
  import rangeSpec from './rangeSpec';
60
60
  import replaceStringParameters from './replaceStringParameters';
61
+ import resolveUiSchema, { expandUiSchemaDefinitions } from './resolveUiSchema';
61
62
  import schemaRequiresTrueValue from './schemaRequiresTrueValue';
62
63
  import shouldRender, { ComponentUpdateStrategy } from './shouldRender';
63
64
  import shouldRenderOptionalField from './shouldRenderOptionalField';
@@ -152,6 +153,8 @@ export {
152
153
  parseDateString,
153
154
  rangeSpec,
154
155
  replaceStringParameters,
156
+ resolveUiSchema,
157
+ expandUiSchemaDefinitions,
155
158
  schemaRequiresTrueValue,
156
159
  shallowEquals,
157
160
  shouldRender,
@@ -29,9 +29,11 @@ export type SchemaMap<S extends StrictRJSFSchema = RJSFSchema> = {
29
29
  * the hashed value of the schema. NOTE: After hashing the schema, an $id with the hash value is added to the
30
30
  * schema IF that schema doesn't already have an $id, prior to putting the schema into the map.
31
31
  */
32
- export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
33
- implements ValidatorType<T, S, F>
34
- {
32
+ export default class ParserValidator<
33
+ T = any,
34
+ S extends StrictRJSFSchema = RJSFSchema,
35
+ F extends FormContextType = any,
36
+ > implements ValidatorType<T, S, F> {
35
37
  /** The rootSchema provided during construction of the class */
36
38
  readonly rootSchema: S;
37
39
 
@@ -0,0 +1,156 @@
1
+ import {
2
+ ADDITIONAL_PROPERTIES_KEY,
3
+ ALL_OF_KEY,
4
+ ANY_OF_KEY,
5
+ ITEMS_KEY,
6
+ ONE_OF_KEY,
7
+ PROPERTIES_KEY,
8
+ REF_KEY,
9
+ } from './constants';
10
+ import findSchemaDefinition from './findSchemaDefinition';
11
+ import isObject from './isObject';
12
+ import mergeObjects from './mergeObjects';
13
+ import { FormContextType, GenericObjectType, Registry, RJSFSchema, StrictRJSFSchema, UiSchema } from './types';
14
+
15
+ // Keywords where child schemas map to uiSchema at the SAME key
16
+ const SAME_KEY_KEYWORDS = [ITEMS_KEY, ADDITIONAL_PROPERTIES_KEY] as const;
17
+
18
+ // Keywords where child schemas are in an array, each mapping to uiSchema[keyword][i]
19
+ const ARRAY_KEYWORDS = [ONE_OF_KEY, ANY_OF_KEY, ALL_OF_KEY] as const;
20
+
21
+ /** Expands `ui:definitions` into the uiSchema by walking the schema tree and finding all `$ref`s.
22
+ * Called once at form initialization to pre-expand definitions into the uiSchema structure.
23
+ *
24
+ * For recursive schemas, expansion stops at recursion points to avoid infinite loops.
25
+ * Runtime resolution via `resolveUiSchema` handles these cases using registry definitions.
26
+ *
27
+ * @param currentSchema - The current schema node being processed
28
+ * @param uiSchema - The uiSchema at the current path
29
+ * @param registry - The registry containing rootSchema and uiSchemaDefinitions
30
+ * @param visited - Set of $refs already visited (to detect recursion)
31
+ * @returns - The expanded uiSchema with definitions merged in
32
+ */
33
+ export function expandUiSchemaDefinitions<
34
+ T = any,
35
+ S extends StrictRJSFSchema = RJSFSchema,
36
+ F extends FormContextType = any,
37
+ >(
38
+ currentSchema: S,
39
+ uiSchema: UiSchema<T, S, F>,
40
+ registry: Registry<T, S, F>,
41
+ visited: Set<string> = new Set(),
42
+ ): UiSchema<T, S, F> {
43
+ const { rootSchema, uiSchemaDefinitions: definitions } = registry;
44
+ let result = { ...uiSchema };
45
+ let resolvedSchema = currentSchema;
46
+
47
+ const ref = currentSchema[REF_KEY] as string | undefined;
48
+ const isRecursive = ref && visited.has(ref);
49
+
50
+ if (ref) {
51
+ visited.add(ref);
52
+
53
+ if (definitions && ref in definitions) {
54
+ result = mergeObjects(definitions[ref] as GenericObjectType, result as GenericObjectType) as UiSchema<T, S, F>;
55
+ }
56
+
57
+ if (isRecursive) {
58
+ return result;
59
+ }
60
+
61
+ try {
62
+ resolvedSchema = findSchemaDefinition<S>(ref, rootSchema as S);
63
+ } catch {
64
+ resolvedSchema = currentSchema;
65
+ }
66
+ }
67
+
68
+ // Process properties (each property maps to uiSchema[propName] - flattened)
69
+ const properties = resolvedSchema[PROPERTIES_KEY];
70
+ if (properties && isObject(properties)) {
71
+ for (const [propName, propSchema] of Object.entries(properties as Record<string, S>)) {
72
+ const propUiSchema = (result[propName] || {}) as UiSchema<T, S, F>;
73
+ const expanded = expandUiSchemaDefinitions(propSchema, propUiSchema, registry, new Set(visited));
74
+ if (Object.keys(expanded).length > 0) {
75
+ result[propName] = expanded;
76
+ }
77
+ }
78
+ }
79
+
80
+ // Process keywords where child maps to same key in uiSchema (items, additionalProperties)
81
+ for (const keyword of SAME_KEY_KEYWORDS) {
82
+ const subSchema = resolvedSchema[keyword];
83
+ if (subSchema && isObject(subSchema) && !Array.isArray(subSchema)) {
84
+ const currentUiSchema = result[keyword];
85
+ if (typeof currentUiSchema !== 'function') {
86
+ const subUiSchema = ((currentUiSchema as GenericObjectType) || {}) as UiSchema<T, S, F>;
87
+ const expanded = expandUiSchemaDefinitions(subSchema as S, subUiSchema, registry, new Set(visited));
88
+ if (Object.keys(expanded).length > 0) {
89
+ (result as GenericObjectType)[keyword] = expanded;
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ // Process array keywords (oneOf, anyOf, allOf) - each option maps to uiSchema[keyword][i]
96
+ for (const keyword of ARRAY_KEYWORDS) {
97
+ const schemaOptions = resolvedSchema[keyword];
98
+ if (Array.isArray(schemaOptions) && schemaOptions.length > 0) {
99
+ const currentUiSchemaArray = (result as GenericObjectType)[keyword];
100
+ const uiSchemaArray: UiSchema<T, S, F>[] = Array.isArray(currentUiSchemaArray) ? [...currentUiSchemaArray] : [];
101
+
102
+ let hasExpanded = false;
103
+ for (let i = 0; i < schemaOptions.length; i++) {
104
+ const optionSchema = schemaOptions[i] as S;
105
+ const optionUiSchema = (uiSchemaArray[i] || {}) as UiSchema<T, S, F>;
106
+ const expanded = expandUiSchemaDefinitions(optionSchema, optionUiSchema, registry, new Set(visited));
107
+ if (Object.keys(expanded).length > 0) {
108
+ uiSchemaArray[i] = expanded;
109
+ hasExpanded = true;
110
+ }
111
+ }
112
+
113
+ if (hasExpanded) {
114
+ (result as GenericObjectType)[keyword] = uiSchemaArray;
115
+ }
116
+ }
117
+ }
118
+
119
+ return result;
120
+ }
121
+
122
+ /** Resolves the uiSchema for a given schema, considering `ui:definitions` stored in the registry.
123
+ *
124
+ * This function is called at runtime for each field. It handles recursive schemas where the
125
+ * pre-expansion in `expandUiSchemaDefinitions` couldn't go deeper.
126
+ *
127
+ * When the schema contains a `$ref`, this function looks up the corresponding uiSchema definition
128
+ * from `registry.uiSchemaDefinitions` and merges it with any local uiSchema overrides.
129
+ *
130
+ * Resolution order (later sources override earlier):
131
+ * 1. `ui:definitions[$ref]` - base definition from registry
132
+ * 2. `localUiSchema` - local overrides at current path
133
+ *
134
+ * @param schema - The JSON schema (may still contain `$ref` for recursive schemas)
135
+ * @param localUiSchema - The uiSchema at the current path (local overrides)
136
+ * @param registry - The registry containing `uiSchemaDefinitions`
137
+ * @returns - The resolved uiSchema with definitions merged in
138
+ */
139
+ export default function resolveUiSchema<
140
+ T = any,
141
+ S extends StrictRJSFSchema = RJSFSchema,
142
+ F extends FormContextType = any,
143
+ >(schema: S, localUiSchema: UiSchema<T, S, F> | undefined, registry: Registry<T, S, F>): UiSchema<T, S, F> {
144
+ const ref = schema[REF_KEY] as string | undefined;
145
+ const definitionUiSchema = ref ? registry.uiSchemaDefinitions?.[ref] : undefined;
146
+
147
+ if (!definitionUiSchema) {
148
+ return localUiSchema || {};
149
+ }
150
+
151
+ if (!localUiSchema || Object.keys(localUiSchema).length === 0) {
152
+ return { ...definitionUiSchema };
153
+ }
154
+
155
+ return mergeObjects(definitionUiSchema as GenericObjectType, localUiSchema as GenericObjectType) as UiSchema<T, S, F>;
156
+ }
package/src/types.ts CHANGED
@@ -478,11 +478,16 @@ export interface Registry<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
478
478
  readonly globalFormOptions: GlobalFormOptions;
479
479
  /** The optional global UI Options that are available for all templates, fields and widgets to access */
480
480
  globalUiOptions?: GlobalUISchemaOptions;
481
+ /** The optional uiSchema definitions extracted from the root uiSchema, keyed by `$ref` paths.
482
+ * Used to automatically apply uiSchema when a schema with a matching `$ref` is resolved.
483
+ */
484
+ uiSchemaDefinitions?: UiSchemaDefinitions<T, S, F>;
481
485
  }
482
486
 
483
487
  /** The properties that are passed to a `Field` implementation */
484
488
  export interface FieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
485
- extends GenericObjectType,
489
+ extends
490
+ GenericObjectType,
486
491
  RJSFBaseProps<T, S, F>,
487
492
  Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
488
493
  /** The FieldPathId of the field in the hierarchy */
@@ -896,7 +901,8 @@ export interface MultiSchemaFieldTemplateProps<
896
901
 
897
902
  /** The properties that are passed to a `Widget` implementation */
898
903
  export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
899
- extends GenericObjectType,
904
+ extends
905
+ GenericObjectType,
900
906
  RJSFBaseProps<T, S, F>,
901
907
  Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
902
908
  /** The generated id for this widget, used to provide unique `name`s and `id`s for the HTML field elements rendered by
@@ -1113,6 +1119,15 @@ export type UIOptionsType<
1113
1119
  */
1114
1120
  export type ArrayElement<A> = A extends readonly (infer E)[] ? E : A;
1115
1121
 
1122
+ /** Type describing the uiSchema definitions that can be applied to schemas referenced by `$ref`.
1123
+ * Keys are the full `$ref` path (e.g., '#/$defs/node', '#/definitions/address').
1124
+ * When a schema with a matching `$ref` is resolved, the corresponding uiSchema definition
1125
+ * is automatically applied and merged with any local uiSchema overrides.
1126
+ */
1127
+ export type UiSchemaDefinitions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = {
1128
+ [refPath: string]: UiSchema<T, S, F>;
1129
+ };
1130
+
1116
1131
  /** Type describing the well-known properties of the `UiSchema` while also supporting all user defined properties,
1117
1132
  * starting with `ui:`.
1118
1133
  */
@@ -1148,6 +1163,12 @@ export type UiSchema<
1148
1163
  items?:
1149
1164
  | UiSchema<ArrayElement<T>, S, F>
1150
1165
  | ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
1166
+ /** An object containing uiSchema definitions keyed by JSON Schema `$ref` paths.
1167
+ * When a schema with a `$ref` is resolved, the corresponding uiSchema definition is automatically
1168
+ * applied and merged with any local uiSchema overrides at that path.
1169
+ * Keys must be full `$ref` paths (e.g., '#/$defs/node', '#/definitions/address').
1170
+ */
1171
+ 'ui:definitions'?: UiSchemaDefinitions<T, S, F>;
1151
1172
  };
1152
1173
 
1153
1174
  /** A `CustomValidator` function takes in a `formData`, `errors`, `uiSchema` and `errorSchema` objects and returns the given `errors`