@rjsf/utils 5.22.3 → 5.23.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.
Files changed (54) hide show
  1. package/dist/index.js +175 -59
  2. package/dist/index.js.map +2 -2
  3. package/dist/utils.esm.js +175 -59
  4. package/dist/utils.esm.js.map +2 -2
  5. package/dist/utils.umd.js +187 -63
  6. package/lib/ErrorSchemaBuilder.js +3 -3
  7. package/lib/ErrorSchemaBuilder.js.map +1 -1
  8. package/lib/createSchemaUtils.js +7 -7
  9. package/lib/createSchemaUtils.js.map +1 -1
  10. package/lib/schema/getClosestMatchingOption.d.ts +5 -3
  11. package/lib/schema/getClosestMatchingOption.js +11 -7
  12. package/lib/schema/getClosestMatchingOption.js.map +1 -1
  13. package/lib/schema/getDefaultFormState.d.ts +1 -1
  14. package/lib/schema/getDefaultFormState.js +40 -16
  15. package/lib/schema/getDefaultFormState.js.map +1 -1
  16. package/lib/schema/getDisplayLabel.d.ts +3 -2
  17. package/lib/schema/getDisplayLabel.js +4 -3
  18. package/lib/schema/getDisplayLabel.js.map +1 -1
  19. package/lib/schema/isFilesArray.d.ts +3 -2
  20. package/lib/schema/isFilesArray.js +3 -2
  21. package/lib/schema/isFilesArray.js.map +1 -1
  22. package/lib/schema/isMultiSelect.d.ts +3 -2
  23. package/lib/schema/isMultiSelect.js +3 -2
  24. package/lib/schema/isMultiSelect.js.map +1 -1
  25. package/lib/schema/isSelect.d.ts +3 -2
  26. package/lib/schema/isSelect.js +3 -2
  27. package/lib/schema/isSelect.js.map +1 -1
  28. package/lib/schema/retrieveSchema.d.ts +2 -1
  29. package/lib/schema/retrieveSchema.js +5 -4
  30. package/lib/schema/retrieveSchema.js.map +1 -1
  31. package/lib/schema/sanitizeDataForNewSchema.d.ts +3 -2
  32. package/lib/schema/sanitizeDataForNewSchema.js +8 -7
  33. package/lib/schema/sanitizeDataForNewSchema.js.map +1 -1
  34. package/lib/schema/toIdSchema.js +1 -1
  35. package/lib/schema/toIdSchema.js.map +1 -1
  36. package/lib/schema/toPathSchema.d.ts +3 -2
  37. package/lib/schema/toPathSchema.js +13 -11
  38. package/lib/schema/toPathSchema.js.map +1 -1
  39. package/lib/tsconfig.tsbuildinfo +1 -1
  40. package/lib/types.d.ts +12 -0
  41. package/package.json +2 -2
  42. package/src/ErrorSchemaBuilder.ts +3 -3
  43. package/src/createSchemaUtils.ts +29 -7
  44. package/src/schema/getClosestMatchingOption.ts +31 -8
  45. package/src/schema/getDefaultFormState.ts +48 -15
  46. package/src/schema/getDisplayLabel.ts +6 -3
  47. package/src/schema/isFilesArray.ts +18 -3
  48. package/src/schema/isMultiSelect.ts +9 -3
  49. package/src/schema/isSelect.ts +5 -3
  50. package/src/schema/retrieveSchema.ts +19 -4
  51. package/src/schema/sanitizeDataForNewSchema.ts +49 -8
  52. package/src/schema/toIdSchema.ts +1 -1
  53. package/src/schema/toPathSchema.ts +45 -12
  54. package/src/types.ts +12 -0
@@ -10,7 +10,7 @@ import getFirstMatchingOption from './getFirstMatchingOption';
10
10
  import retrieveSchema, { resolveAllReferences } from './retrieveSchema';
11
11
  import { ONE_OF_KEY, REF_KEY, JUNK_OPTION_ID, ANY_OF_KEY } from '../constants';
12
12
  import guessType from '../guessType';
13
- import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
13
+ import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
14
14
  import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
15
15
  import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator';
16
16
 
@@ -45,13 +45,15 @@ export const JUNK_OPTION: StrictRJSFSchema = {
45
45
  * @param rootSchema - The root JSON schema of the entire form
46
46
  * @param schema - The schema for which the score is being calculated
47
47
  * @param formData - The form data associated with the schema, used to calculate the score
48
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
48
49
  * @returns - The score a schema against the formData
49
50
  */
50
51
  export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
51
52
  validator: ValidatorType<T, S, F>,
52
53
  rootSchema: S,
53
54
  schema?: S,
54
- formData?: any
55
+ formData?: any,
56
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
55
57
  ): number {
56
58
  let totalScore = 0;
57
59
  if (schema) {
@@ -64,8 +66,23 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
64
66
  return score;
65
67
  }
66
68
  if (has(value, REF_KEY)) {
67
- const newSchema = retrieveSchema<T, S, F>(validator, value as S, rootSchema, formValue);
68
- return score + calculateIndexScore<T, S, F>(validator, rootSchema, newSchema, formValue || {});
69
+ const newSchema = retrieveSchema<T, S, F>(
70
+ validator,
71
+ value as S,
72
+ rootSchema,
73
+ formValue,
74
+ experimental_customMergeAllOf
75
+ );
76
+ return (
77
+ score +
78
+ calculateIndexScore<T, S, F>(
79
+ validator,
80
+ rootSchema,
81
+ newSchema,
82
+ formValue || {},
83
+ experimental_customMergeAllOf
84
+ )
85
+ );
69
86
  }
70
87
  if ((has(value, ONE_OF_KEY) || has(value, ANY_OF_KEY)) && formValue) {
71
88
  const key = has(value, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
@@ -78,7 +95,8 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
78
95
  formValue,
79
96
  get(value, key) as S[],
80
97
  -1,
81
- discriminator
98
+ discriminator,
99
+ experimental_customMergeAllOf
82
100
  )
83
101
  );
84
102
  }
@@ -87,7 +105,10 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
87
105
  // If the structure is matching then give it a little boost in score
88
106
  score += 1;
89
107
  }
90
- return score + calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue);
108
+ return (
109
+ score +
110
+ calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue, experimental_customMergeAllOf)
111
+ );
91
112
  }
92
113
  if (value.type === guessType(formValue)) {
93
114
  // If the types match, then we bump the score by one
@@ -135,6 +156,7 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
135
156
  * @param [selectedOption=-1] - The index of the currently selected option, defaulted to -1 if not specified
136
157
  * @param [discriminatorField] - The optional name of the field within the options object whose value is used to
137
158
  * determine which option is selected
159
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
138
160
  * @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match
139
161
  */
140
162
  export default function getClosestMatchingOption<
@@ -147,7 +169,8 @@ export default function getClosestMatchingOption<
147
169
  formData: T | undefined,
148
170
  options: S[],
149
171
  selectedOption = -1,
150
- discriminatorField?: string
172
+ discriminatorField?: string,
173
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
151
174
  ): number {
152
175
  // First resolve any refs in the options
153
176
  const resolvedOptions = options.map((option) => {
@@ -185,7 +208,7 @@ export default function getClosestMatchingOption<
185
208
  (scoreData: BestType, index: number) => {
186
209
  const { bestScore } = scoreData;
187
210
  const option = resolvedOptions[index];
188
- const score = calculateIndexScore(validator, rootSchema, option, formData);
211
+ const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf);
189
212
  scoreCount.add(score);
190
213
  if (score > bestScore) {
191
214
  return { bestIndex: index, bestScore: score };
@@ -31,9 +31,10 @@ import {
31
31
  } from '../types';
32
32
  import isMultiSelect from './isMultiSelect';
33
33
  import retrieveSchema, { resolveDependencies } from './retrieveSchema';
34
- import isConstant from '../isConstant';
35
34
  import { JSONSchema7Object } from 'json-schema';
36
35
 
36
+ const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
37
+
37
38
  /** Enum that indicates how `schema.additionalItems` should be handled by the `getInnerSchemaForArrayItem()` function.
38
39
  */
39
40
  export enum AdditionalItemsHandling {
@@ -199,9 +200,10 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
199
200
  let defaults: T | T[] | undefined = parentDefaults;
200
201
  // If we get a new schema, then we need to recompute defaults again for the new schema found.
201
202
  let schemaToCompute: S | null = null;
203
+ let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
202
204
  let updatedRecurseList = _recurseList;
203
205
 
204
- if (isConstant(schema)) {
206
+ if (schema[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults !== 'never') {
205
207
  defaults = schema.const as unknown as T;
206
208
  } else if (isObject(defaults) && isObject(schema.default)) {
207
209
  // For object defaults, only override parent defaults that are defined in
@@ -239,6 +241,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
239
241
  includeUndefinedValues,
240
242
  _recurseList,
241
243
  experimental_defaultFormStateBehavior,
244
+ experimental_customMergeAllOf,
242
245
  parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
243
246
  rawFormData: formData as T,
244
247
  required,
@@ -250,6 +253,15 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
250
253
  return undefined;
251
254
  }
252
255
  const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
256
+ const { type = 'null' } = remaining;
257
+ if (
258
+ !Array.isArray(type) &&
259
+ PRIMITIVE_TYPES.includes(type) &&
260
+ experimental_dfsb_to_compute?.constAsDefaults === 'skipOneOf'
261
+ ) {
262
+ // If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
263
+ experimental_dfsb_to_compute = { ...experimental_dfsb_to_compute, constAsDefaults: 'never' };
264
+ }
253
265
  schemaToCompute = oneOf![
254
266
  getClosestMatchingOption<T, S, F>(
255
267
  validator,
@@ -257,7 +269,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
257
269
  isEmpty(formData) ? undefined : formData,
258
270
  oneOf as S[],
259
271
  0,
260
- discriminator
272
+ discriminator,
273
+ experimental_customMergeAllOf
261
274
  )
262
275
  ] as S;
263
276
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -274,7 +287,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
274
287
  isEmpty(formData) ? undefined : formData,
275
288
  anyOf as S[],
276
289
  0,
277
- discriminator
290
+ discriminator,
291
+ experimental_customMergeAllOf
278
292
  )
279
293
  ] as S;
280
294
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -285,7 +299,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
285
299
  rootSchema,
286
300
  includeUndefinedValues,
287
301
  _recurseList: updatedRecurseList,
288
- experimental_defaultFormStateBehavior,
302
+ experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
303
+ experimental_customMergeAllOf,
289
304
  parentDefaults: defaults as T | undefined,
290
305
  rawFormData: formData as T,
291
306
  required,
@@ -337,9 +352,12 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
337
352
  const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
338
353
  (acc: GenericObjectType, key: string) => {
339
354
  const propertySchema = get(retrievedSchema, [PROPERTIES_KEY, key]);
340
- // Check if the parent schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
355
+ // Check if the parent schema has a const property defined AND we are supporting const as defaults, then we
356
+ // should always return the computedDefault since it's coming from the const.
341
357
  const hasParentConst = isObject(parentConst) && (parentConst as JSONSchema7Object)[key] !== undefined;
342
- const hasConst = (isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst;
358
+ const hasConst =
359
+ ((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) &&
360
+ experimental_defaultFormStateBehavior?.constAsDefaults !== 'never';
343
361
  // Compute the defaults for this node, with the parent defaults we might
344
362
  // have from a previous run: defaults[key].
345
363
  const computedDefault = computeDefaults<T, S, F>(validator, propertySchema, {
@@ -390,6 +408,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
390
408
  rootSchema,
391
409
  _recurseList,
392
410
  experimental_defaultFormStateBehavior,
411
+ experimental_customMergeAllOf,
393
412
  includeUndefinedValues: includeUndefinedValues === true,
394
413
  parentDefaults: get(defaults, [key]),
395
414
  rawFormData: get(formData, [key]),
@@ -426,17 +445,21 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
426
445
  rootSchema = {} as S,
427
446
  _recurseList = [],
428
447
  experimental_defaultFormStateBehavior = undefined,
448
+ experimental_customMergeAllOf = undefined,
429
449
  required,
430
450
  }: ComputeDefaultsProps<T, S> = {},
431
451
  defaults?: T | T[] | undefined
432
452
  ): T | T[] | undefined {
433
453
  const schema: S = rawSchema;
434
454
 
435
- const neverPopulate = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'never';
436
- const ignoreMinItemsFlagSet = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'requiredOnly';
455
+ const arrayMinItemsStateBehavior = experimental_defaultFormStateBehavior?.arrayMinItems ?? {};
456
+ const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior;
457
+
458
+ const neverPopulate = arrayMinItemsPopulate === 'never';
459
+ const ignoreMinItemsFlagSet = arrayMinItemsPopulate === 'requiredOnly';
460
+ const isPopulateAll = arrayMinItemsPopulate === 'all' || (!neverPopulate && !ignoreMinItemsFlagSet);
461
+ const computeSkipPopulate = arrayMinItemsStateBehavior?.computeSkipPopulate ?? (() => false);
437
462
  const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
438
- const computeSkipPopulate =
439
- experimental_defaultFormStateBehavior?.arrayMinItems?.computeSkipPopulate ?? (() => false);
440
463
 
441
464
  const emptyDefault = isSkipEmptyDefaults ? undefined : [];
442
465
 
@@ -448,6 +471,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
448
471
  rootSchema,
449
472
  _recurseList,
450
473
  experimental_defaultFormStateBehavior,
474
+ experimental_customMergeAllOf,
451
475
  parentDefaults: item,
452
476
  required,
453
477
  });
@@ -460,21 +484,29 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
460
484
  if (neverPopulate) {
461
485
  defaults = rawFormData;
462
486
  } else {
463
- defaults = rawFormData.map((item: T, idx: number) => {
487
+ const itemDefaults = rawFormData.map((item: T, idx: number) => {
464
488
  return computeDefaults<T, S, F>(validator, schemaItem, {
465
489
  rootSchema,
466
490
  _recurseList,
467
491
  experimental_defaultFormStateBehavior,
492
+ experimental_customMergeAllOf,
468
493
  rawFormData: item,
469
494
  parentDefaults: get(defaults, [idx]),
470
495
  required,
471
496
  });
472
497
  }) as T[];
498
+
499
+ // If the populate 'requiredOnly' flag is set then we only merge and include extra defaults if they are required.
500
+ // Or if populate 'all' is set we merge and include extra defaults.
501
+ const mergeExtraDefaults = ((ignoreMinItemsFlagSet && required) || isPopulateAll) && arrayMergeExtraDefaults;
502
+ defaults = mergeDefaultsWithFormData(defaults, itemDefaults, mergeExtraDefaults);
473
503
  }
474
504
  }
475
505
 
476
- // Check if the schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
477
- const hasConst = isObject(schema) && CONST_KEY in schema;
506
+ // Check if the schema has a const property defined AND we are supporting const as defaults, then we should always
507
+ // return the computedDefault since it's coming from the const.
508
+ const hasConst =
509
+ isObject(schema) && CONST_KEY in schema && experimental_defaultFormStateBehavior?.constAsDefaults !== 'never';
478
510
  if (hasConst === false) {
479
511
  if (neverPopulate) {
480
512
  return defaults ?? emptyDefault;
@@ -489,7 +521,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
489
521
  const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
490
522
  if (
491
523
  !schema.minItems ||
492
- isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
524
+ isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
493
525
  computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
494
526
  schema.minItems <= defaultsLength
495
527
  ) {
@@ -507,6 +539,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
507
539
  rootSchema,
508
540
  _recurseList,
509
541
  experimental_defaultFormStateBehavior,
542
+ experimental_customMergeAllOf,
510
543
  required,
511
544
  })
512
545
  ) as T[];
@@ -9,6 +9,7 @@ import {
9
9
  StrictRJSFSchema,
10
10
  UiSchema,
11
11
  ValidatorType,
12
+ Experimental_CustomMergeAllOf,
12
13
  } from '../types';
13
14
  import isFilesArray from './isFilesArray';
14
15
  import isMultiSelect from './isMultiSelect';
@@ -21,6 +22,7 @@ import isMultiSelect from './isMultiSelect';
21
22
  * @param [uiSchema={}] - The UI schema from which to derive potentially displayable information
22
23
  * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
23
24
  * @param [globalOptions={}] - The optional Global UI Schema from which to get any fallback `xxx` options
25
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
24
26
  * @returns - True if the label should be displayed or false if it should not
25
27
  */
26
28
  export default function getDisplayLabel<
@@ -32,7 +34,8 @@ export default function getDisplayLabel<
32
34
  schema: S,
33
35
  uiSchema: UiSchema<T, S, F> = {},
34
36
  rootSchema?: S,
35
- globalOptions?: GlobalUISchemaOptions
37
+ globalOptions?: GlobalUISchemaOptions,
38
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
36
39
  ): boolean {
37
40
  const uiOptions = getUiOptions<T, S, F>(uiSchema, globalOptions);
38
41
  const { label = true } = uiOptions;
@@ -41,8 +44,8 @@ export default function getDisplayLabel<
41
44
 
42
45
  if (schemaType === 'array') {
43
46
  displayLabel =
44
- isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
45
- isFilesArray<T, S, F>(validator, schema, uiSchema, rootSchema) ||
47
+ isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
48
+ isFilesArray<T, S, F>(validator, schema, uiSchema, rootSchema, experimental_customMergeAllOf) ||
46
49
  isCustomWidget(uiSchema);
47
50
  }
48
51
 
@@ -1,5 +1,12 @@
1
1
  import { UI_WIDGET_KEY } from '../constants';
2
- import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType } from '../types';
2
+ import {
3
+ Experimental_CustomMergeAllOf,
4
+ FormContextType,
5
+ RJSFSchema,
6
+ StrictRJSFSchema,
7
+ UiSchema,
8
+ ValidatorType,
9
+ } from '../types';
3
10
  import retrieveSchema from './retrieveSchema';
4
11
 
5
12
  /** Checks to see if the `schema` and `uiSchema` combination represents an array of files
@@ -8,19 +15,27 @@ import retrieveSchema from './retrieveSchema';
8
15
  * @param schema - The schema for which check for array of files flag is desired
9
16
  * @param [uiSchema={}] - The UI schema from which to check the widget
10
17
  * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
18
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
11
19
  * @returns - True if schema/uiSchema contains an array of files, otherwise false
12
20
  */
13
21
  export default function isFilesArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
14
22
  validator: ValidatorType<T, S, F>,
15
23
  schema: S,
16
24
  uiSchema: UiSchema<T, S, F> = {},
17
- rootSchema?: S
25
+ rootSchema?: S,
26
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
18
27
  ) {
19
28
  if (uiSchema[UI_WIDGET_KEY] === 'files') {
20
29
  return true;
21
30
  }
22
31
  if (schema.items) {
23
- const itemsSchema = retrieveSchema<T, S, F>(validator, schema.items as S, rootSchema);
32
+ const itemsSchema = retrieveSchema<T, S, F>(
33
+ validator,
34
+ schema.items as S,
35
+ rootSchema,
36
+ undefined,
37
+ experimental_customMergeAllOf
38
+ );
24
39
  return itemsSchema.type === 'string' && itemsSchema.format === 'data-url';
25
40
  }
26
41
  return false;
@@ -1,4 +1,4 @@
1
- import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
1
+ import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types';
2
2
 
3
3
  import isSelect from './isSelect';
4
4
 
@@ -7,15 +7,21 @@ import isSelect from './isSelect';
7
7
  * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
8
8
  * @param schema - The schema for which check for a multi-select flag is desired
9
9
  * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
10
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
10
11
  * @returns - True if schema contains a multi-select, otherwise false
11
12
  */
12
13
  export default function isMultiSelect<
13
14
  T = any,
14
15
  S extends StrictRJSFSchema = RJSFSchema,
15
16
  F extends FormContextType = any
16
- >(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S) {
17
+ >(
18
+ validator: ValidatorType<T, S, F>,
19
+ schema: S,
20
+ rootSchema?: S,
21
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
22
+ ) {
17
23
  if (!schema.uniqueItems || !schema.items || typeof schema.items === 'boolean') {
18
24
  return false;
19
25
  }
20
- return isSelect<T, S, F>(validator, schema.items as S, rootSchema);
26
+ return isSelect<T, S, F>(validator, schema.items as S, rootSchema, experimental_customMergeAllOf);
21
27
  }
@@ -1,5 +1,5 @@
1
1
  import isConstant from '../isConstant';
2
- import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
2
+ import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types';
3
3
  import retrieveSchema from './retrieveSchema';
4
4
 
5
5
  /** Checks to see if the `schema` combination represents a select
@@ -7,14 +7,16 @@ import retrieveSchema from './retrieveSchema';
7
7
  * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
8
8
  * @param theSchema - The schema for which check for a select flag is desired
9
9
  * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
10
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
10
11
  * @returns - True if schema contains a select, otherwise false
11
12
  */
12
13
  export default function isSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
13
14
  validator: ValidatorType<T, S, F>,
14
15
  theSchema: S,
15
- rootSchema: S = {} as S
16
+ rootSchema: S = {} as S,
17
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
16
18
  ) {
17
- const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, undefined);
19
+ const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, undefined, experimental_customMergeAllOf);
18
20
  const altSchemas = schema.oneOf || schema.anyOf;
19
21
  if (Array.isArray(schema.enum)) {
20
22
  return true;
@@ -365,13 +365,20 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
365
365
  * @param theSchema - The schema for which the existing additional properties is desired
366
366
  * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s * @param validator
367
367
  * @param [aFormData] - The current formData, if any, to assist retrieving a schema
368
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
368
369
  * @returns - The updated schema with additional properties stubbed
369
370
  */
370
371
  export function stubExistingAdditionalProperties<
371
372
  T = any,
372
373
  S extends StrictRJSFSchema = RJSFSchema,
373
374
  F extends FormContextType = any
374
- >(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S, aFormData?: T): S {
375
+ >(
376
+ validator: ValidatorType<T, S, F>,
377
+ theSchema: S,
378
+ rootSchema?: S,
379
+ aFormData?: T,
380
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
381
+ ): S {
375
382
  // Clone the schema so that we don't ruin the consumer's original
376
383
  const schema = {
377
384
  ...theSchema,
@@ -393,7 +400,8 @@ export function stubExistingAdditionalProperties<
393
400
  validator,
394
401
  { $ref: get(schema.additionalProperties, [REF_KEY]) } as S,
395
402
  rootSchema,
396
- formData as T
403
+ formData as T,
404
+ experimental_customMergeAllOf
397
405
  );
398
406
  } else if ('type' in schema.additionalProperties!) {
399
407
  additionalProperties = { ...schema.additionalProperties };
@@ -456,7 +464,8 @@ export function retrieveSchemaInternal<
456
464
  rootSchema,
457
465
  expandAllBranches,
458
466
  recurseList,
459
- rawFormData
467
+ rawFormData,
468
+ experimental_customMergeAllOf
460
469
  );
461
470
  return resolvedSchemas.flatMap((s: S) => {
462
471
  let resolvedSchema = s;
@@ -507,7 +516,13 @@ export function retrieveSchemaInternal<
507
516
  const hasAdditionalProperties =
508
517
  ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false;
509
518
  if (hasAdditionalProperties) {
510
- return stubExistingAdditionalProperties<T, S, F>(validator, resolvedSchema, rootSchema, rawFormData as T);
519
+ return stubExistingAdditionalProperties<T, S, F>(
520
+ validator,
521
+ resolvedSchema,
522
+ rootSchema,
523
+ rawFormData as T,
524
+ experimental_customMergeAllOf
525
+ );
511
526
  }
512
527
 
513
528
  return resolvedSchema;
@@ -1,7 +1,14 @@
1
1
  import get from 'lodash/get';
2
2
  import has from 'lodash/has';
3
3
 
4
- import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
4
+ import {
5
+ Experimental_CustomMergeAllOf,
6
+ FormContextType,
7
+ GenericObjectType,
8
+ RJSFSchema,
9
+ StrictRJSFSchema,
10
+ ValidatorType,
11
+ } from '../types';
5
12
  import { PROPERTIES_KEY, REF_KEY } from '../constants';
6
13
  import retrieveSchema from './retrieveSchema';
7
14
 
@@ -51,6 +58,7 @@ const NO_VALUE = Symbol('no Value');
51
58
  * @param [newSchema] - The new schema for which the data is being sanitized
52
59
  * @param [oldSchema] - The old schema from which the data originated
53
60
  * @param [data={}] - The form data associated with the schema, defaulting to an empty object when undefined
61
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
54
62
  * @returns - The new form data, with all the fields uniquely associated with the old schema set
55
63
  * to `undefined`. Will return `undefined` if the new schema is not an object containing properties.
56
64
  */
@@ -58,7 +66,14 @@ export default function sanitizeDataForNewSchema<
58
66
  T = any,
59
67
  S extends StrictRJSFSchema = RJSFSchema,
60
68
  F extends FormContextType = any
61
- >(validator: ValidatorType<T, S, F>, rootSchema: S, newSchema?: S, oldSchema?: S, data: any = {}): T {
69
+ >(
70
+ validator: ValidatorType<T, S, F>,
71
+ rootSchema: S,
72
+ newSchema?: S,
73
+ oldSchema?: S,
74
+ data: any = {},
75
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
76
+ ): T {
62
77
  // By default, we will clear the form data
63
78
  let newFormData;
64
79
  // If the new schema is of type object and that object contains a list of properties
@@ -82,10 +97,22 @@ export default function sanitizeDataForNewSchema<
82
97
  let newKeyedSchema: S = get(newSchema, [PROPERTIES_KEY, key], {});
83
98
  // Resolve the refs if they exist
84
99
  if (has(oldKeyedSchema, REF_KEY)) {
85
- oldKeyedSchema = retrieveSchema<T, S, F>(validator, oldKeyedSchema, rootSchema, formValue);
100
+ oldKeyedSchema = retrieveSchema<T, S, F>(
101
+ validator,
102
+ oldKeyedSchema,
103
+ rootSchema,
104
+ formValue,
105
+ experimental_customMergeAllOf
106
+ );
86
107
  }
87
108
  if (has(newKeyedSchema, REF_KEY)) {
88
- newKeyedSchema = retrieveSchema<T, S, F>(validator, newKeyedSchema, rootSchema, formValue);
109
+ newKeyedSchema = retrieveSchema<T, S, F>(
110
+ validator,
111
+ newKeyedSchema,
112
+ rootSchema,
113
+ formValue,
114
+ experimental_customMergeAllOf
115
+ );
89
116
  }
90
117
  // Now get types and see if they are the same
91
118
  const oldSchemaTypeForKey = get(oldKeyedSchema, 'type');
@@ -104,7 +131,8 @@ export default function sanitizeDataForNewSchema<
104
131
  rootSchema,
105
132
  newKeyedSchema,
106
133
  oldKeyedSchema,
107
- formValue
134
+ formValue,
135
+ experimental_customMergeAllOf
108
136
  );
109
137
  if (itemData !== undefined || newSchemaTypeForKey === 'array') {
110
138
  // only put undefined values for the array type and not the object type
@@ -154,10 +182,22 @@ export default function sanitizeDataForNewSchema<
154
182
  !Array.isArray(newSchemaItems)
155
183
  ) {
156
184
  if (has(oldSchemaItems, REF_KEY)) {
157
- oldSchemaItems = retrieveSchema<T, S, F>(validator, oldSchemaItems as S, rootSchema, data as T);
185
+ oldSchemaItems = retrieveSchema<T, S, F>(
186
+ validator,
187
+ oldSchemaItems as S,
188
+ rootSchema,
189
+ data as T,
190
+ experimental_customMergeAllOf
191
+ );
158
192
  }
159
193
  if (has(newSchemaItems, REF_KEY)) {
160
- newSchemaItems = retrieveSchema<T, S, F>(validator, newSchemaItems as S, rootSchema, data as T);
194
+ newSchemaItems = retrieveSchema<T, S, F>(
195
+ validator,
196
+ newSchemaItems as S,
197
+ rootSchema,
198
+ data as T,
199
+ experimental_customMergeAllOf
200
+ );
161
201
  }
162
202
  // Now get types and see if they are the same
163
203
  const oldSchemaType = get(oldSchemaItems, 'type');
@@ -172,7 +212,8 @@ export default function sanitizeDataForNewSchema<
172
212
  rootSchema,
173
213
  newSchemaItems as S,
174
214
  oldSchemaItems as S,
175
- aValue
215
+ aValue,
216
+ experimental_customMergeAllOf
176
217
  );
177
218
  if (itemValue !== undefined && (maxItems < 0 || newValue.length < maxItems)) {
178
219
  newValue.push(itemValue);
@@ -41,7 +41,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
41
41
  experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
42
42
  ): IdSchema<T> {
43
43
  if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
44
- const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
44
+ const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
45
45
  const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
46
46
  if (sameSchemaIndex === -1) {
47
47
  return toIdSchemaInternal<T, S, F>(