diff --git a/package.json b/package.json index 0639401064..5852ea996c 100644 --- a/package.json +++ b/package.json @@ -79,5 +79,6 @@ "packages/validator-ajv6", "packages/validator-ajv8", "packages/snapshot-tests" - ] + ], + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index 48851cfcf3..a83a629d8f 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -1,10 +1,11 @@ -import { Component, ElementType, FormEvent, ReactNode, Ref, RefObject, createRef } from 'react'; +import { Component, createRef, ElementType, FormEvent, ReactNode, Ref, RefObject } from 'react'; import { createSchemaUtils, CustomValidator, deepEquals, ErrorSchema, ErrorTransformer, + Experimental_DefaultFormStateBehavior, FormContextType, GenericObjectType, getTemplate, @@ -14,31 +15,31 @@ import { mergeObjects, NAME_KEY, PathSchema, - StrictRJSFSchema, Registry, RegistryFieldsType, RegistryWidgetsType, + RJSF_ADDITIONAL_PROPERTIES_FLAG, RJSFSchema, RJSFValidationError, - RJSF_ADDITIONAL_PROPERTIES_FLAG, SchemaUtilsType, shouldRender, + StrictRJSFSchema, SUBMIT_BTN_OPTIONS_KEY, TemplatesType, toErrorList, - UiSchema, UI_GLOBAL_OPTIONS_KEY, UI_OPTIONS_KEY, + UiSchema, ValidationData, validationDataMerge, ValidatorType, - Experimental_DefaultFormStateBehavior, } from '@rjsf/utils'; import _forEach from 'lodash/forEach'; import _get from 'lodash/get'; import _isEmpty from 'lodash/isEmpty'; import _pick from 'lodash/pick'; import _toPath from 'lodash/toPath'; +import fastDeepEqual from 'fast-deep-equal'; import getDefaultRegistry from '../getDefaultRegistry'; @@ -282,7 +283,7 @@ export default class Form< } this.state = this.getStateFromProps(props, props.formData); - if (this.props.onChange && !deepEquals(this.state.formData, this.props.formData)) { + if (this.props.onChange && !fastDeepEqual(this.state.formData, this.props.formData)) { this.props.onChange(this.state); } this.formElement = createRef(); @@ -309,10 +310,14 @@ export default class Form< getSnapshotBeforeUpdate( prevProps: FormProps, prevState: FormState - ): { nextState: FormState; shouldUpdate: true } | { shouldUpdate: false } { + ): + | { nextState: FormState; shouldUpdate: true } + | { + shouldUpdate: false; + } { if (!deepEquals(this.props, prevProps)) { - const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema); - const isFormDataChanged = !deepEquals(prevProps.formData, this.props.formData); + const isSchemaChanged = !fastDeepEqual(prevProps.schema, this.props.schema); + const isFormDataChanged = !fastDeepEqual(prevProps.formData, this.props.formData); const nextState = this.getStateFromProps( this.props, this.props.formData, @@ -346,14 +351,18 @@ export default class Form< componentDidUpdate( _: FormProps, prevState: FormState, - snapshot: { nextState: FormState; shouldUpdate: true } | { shouldUpdate: false } + snapshot: + | { nextState: FormState; shouldUpdate: true } + | { + shouldUpdate: false; + } ) { if (snapshot.shouldUpdate) { const { nextState } = snapshot; if ( - !deepEquals(nextState.formData, this.props.formData) && - !deepEquals(nextState.formData, prevState.formData) && + !fastDeepEqual(nextState.formData, this.props.formData) && + !fastDeepEqual(nextState.formData, prevState.formData) && this.props.onChange ) { this.props.onChange(nextState); @@ -692,7 +701,6 @@ export default class Form< * Callback function to handle reset form data. * - Reset all fields with default values. * - Reset validations and errors - * */ reset = () => { const { onChange } = this.props; @@ -772,7 +780,14 @@ export default class Form< }, () => { if (onSubmit) { - onSubmit({ ...this.state, formData: newFormData, status: 'submitted' }, event); + onSubmit( + { + ...this.state, + formData: newFormData, + status: 'submitted', + }, + event + ); } } ); @@ -956,9 +971,14 @@ export default class Form< let { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions = {} } = getUiOptions(uiSchema); if (disabled) { - submitOptions = { ...submitOptions, props: { ...submitOptions.props, disabled: true } }; + submitOptions = { + ...submitOptions, + props: { ...submitOptions.props, disabled: true }, + }; } - const submitUiSchema = { [UI_OPTIONS_KEY]: { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions } }; + const submitUiSchema = { + [UI_OPTIONS_KEY]: { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions }, + }; return ( = { @@ -67,7 +67,7 @@ class AnyOfField schemaUtils.retrieveSchema(opt, formData)); newState = { selectedOption, retrievedOptions }; } - if (!deepEquals(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) { + if (!fastDeepEqual(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) { const { retrievedOptions } = newState; const matchingOption = this.getMatchingOption(selectedOption, formData, retrievedOptions); diff --git a/packages/utils/src/enumOptionsDeselectValue.ts b/packages/utils/src/enumOptionsDeselectValue.ts index 1dde198c32..1cabb53ccc 100644 --- a/packages/utils/src/enumOptionsDeselectValue.ts +++ b/packages/utils/src/enumOptionsDeselectValue.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; import enumOptionsValueForIndex from './enumOptionsValueForIndex'; @@ -22,7 +22,7 @@ export default function enumOptionsDeselectValue['value'] | EnumOptionsType['value'][] | undefined { const value = enumOptionsValueForIndex(valueIndex, allEnumOptions); if (Array.isArray(selected)) { - return selected.filter((v) => !isEqual(v, value)); + return selected.filter((v) => !fastDeepEqual(v, value)); } - return isEqual(value, selected) ? undefined : selected; + return fastDeepEqual(value, selected) ? undefined : selected; } diff --git a/packages/utils/src/enumOptionsIsSelected.ts b/packages/utils/src/enumOptionsIsSelected.ts index a1c9fed1dd..c5d33ade12 100644 --- a/packages/utils/src/enumOptionsIsSelected.ts +++ b/packages/utils/src/enumOptionsIsSelected.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; @@ -13,7 +13,7 @@ export default function enumOptionsIsSelected['value'] | EnumOptionsType['value'][] ) { if (Array.isArray(selected)) { - return selected.some((sel) => isEqual(sel, value)); + return selected.some((sel) => fastDeepEqual(sel, value)); } - return isEqual(selected, value); + return fastDeepEqual(selected, value); } diff --git a/packages/utils/src/parser/ParserValidator.ts b/packages/utils/src/parser/ParserValidator.ts index f1b771fc39..fd3b513d40 100644 --- a/packages/utils/src/parser/ParserValidator.ts +++ b/packages/utils/src/parser/ParserValidator.ts @@ -1,5 +1,5 @@ import get from 'lodash/get'; -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { ID_KEY } from '../constants'; import hashForSchema from '../hashForSchema'; @@ -67,7 +67,7 @@ export default class ParserValidator(schema)); diff --git a/packages/utils/src/parser/schemaParser.ts b/packages/utils/src/parser/schemaParser.ts index 70151f06a0..2514a482f3 100644 --- a/packages/utils/src/parser/schemaParser.ts +++ b/packages/utils/src/parser/schemaParser.ts @@ -1,10 +1,10 @@ import forEach from 'lodash/forEach'; -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types'; -import { PROPERTIES_KEY, ITEMS_KEY } from '../constants'; +import { ITEMS_KEY, PROPERTIES_KEY } from '../constants'; import ParserValidator, { SchemaMap } from './ParserValidator'; -import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema'; +import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema'; /** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to * capture the sub-schemas that the `isValid()` function is called with. For each schema returned by the @@ -24,7 +24,7 @@ function parseSchema(validator, schema, rootSchema, undefined, true); schemas.forEach((schema) => { - const sameSchemaIndex = recurseList.findIndex((item) => isEqual(item, schema)); + const sameSchemaIndex = recurseList.findIndex((item) => fastDeepEqual(item, schema)); if (sameSchemaIndex === -1) { recurseList.push(schema); const allOptions = resolveAnyOrOneOfSchemas(validator, schema, rootSchema, true); diff --git a/packages/utils/src/schema/retrieveSchema.ts b/packages/utils/src/schema/retrieveSchema.ts index 0f46632f1c..c74690b25c 100644 --- a/packages/utils/src/schema/retrieveSchema.ts +++ b/packages/utils/src/schema/retrieveSchema.ts @@ -1,5 +1,5 @@ import get from 'lodash/get'; -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import set from 'lodash/set'; import times from 'lodash/times'; import transform from 'lodash/transform'; @@ -15,10 +15,10 @@ import { ANY_OF_KEY, DEPENDENCIES_KEY, IF_KEY, + ITEMS_KEY, ONE_OF_KEY, - REF_KEY, PROPERTIES_KEY, - ITEMS_KEY, + REF_KEY, } from '../constants'; import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition'; import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; @@ -196,7 +196,10 @@ export function resolveSchema(allOfSchemaElements); - return allPermutations.map((permutation) => ({ ...schema, allOf: permutation })); + return allPermutations.map((permutation) => ({ + ...schema, + allOf: permutation, + })); } // No $ref or dependencies or allOf attribute was found, returning the original schema. return [schema]; @@ -293,7 +296,7 @@ export function resolveAllReferences( }; } - return isEqual(schema, resolvedSchema) ? schema : resolvedSchema; + return fastDeepEqual(schema, resolvedSchema) ? schema : resolvedSchema; } /** Creates new 'properties' items for each key in the `formData` diff --git a/packages/utils/src/schema/toIdSchema.ts b/packages/utils/src/schema/toIdSchema.ts index 3ea196d2c6..5369062223 100644 --- a/packages/utils/src/schema/toIdSchema.ts +++ b/packages/utils/src/schema/toIdSchema.ts @@ -1,5 +1,5 @@ import get from 'lodash/get'; -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants'; import isObject from '../isObject'; @@ -46,7 +46,7 @@ function toIdSchemaInternal(validator, schema, rootSchema, formData); - const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema)); + const sameSchemaIndex = _recurseList.findIndex((item) => fastDeepEqual(item, _schema)); if (sameSchemaIndex === -1) { stack.push({ validator, @@ -112,7 +112,7 @@ function toIdSchemaInternal - // isEqual(item, _schema) + // fastDeepEqual(item, _schema) // ); // if (sameSchemaIndex === -1) { // return toIdSchemaInternal( diff --git a/packages/utils/src/schema/toPathSchema.ts b/packages/utils/src/schema/toPathSchema.ts index 50447c02b9..d21f7a9655 100644 --- a/packages/utils/src/schema/toPathSchema.ts +++ b/packages/utils/src/schema/toPathSchema.ts @@ -1,6 +1,6 @@ -import get from "lodash/get"; -import isEqual from "lodash/isEqual"; -import set from "lodash/set"; +import get from 'lodash/get'; +import fastDeepEqual from 'fast-deep-equal'; +import set from 'lodash/set'; import { ADDITIONAL_PROPERTIES_KEY, @@ -13,18 +13,11 @@ import { PROPERTIES_KEY, REF_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, -} from "../constants"; -import getDiscriminatorFieldFromSchema from "../getDiscriminatorFieldFromSchema"; -import { - FormContextType, - GenericObjectType, - PathSchema, - RJSFSchema, - StrictRJSFSchema, - ValidatorType, -} from "../types"; -import getClosestMatchingOption from "./getClosestMatchingOption"; -import retrieveSchema from "./retrieveSchema"; +} from '../constants'; +import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; +import { FormContextType, GenericObjectType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; +import getClosestMatchingOption from './getClosestMatchingOption'; +import retrieveSchema from './retrieveSchema'; /** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against * infinite recursion @@ -37,17 +30,13 @@ import retrieveSchema from "./retrieveSchema"; * @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion * @returns - The `PathSchema` object for the `schema` */ -function toPathSchemaInternal< - T = any, - S extends StrictRJSFSchema = RJSFSchema, - F extends FormContextType = any, ->( +function toPathSchemaInternal( validator: ValidatorType, schema: S, name: string, rootSchema?: S, formData?: T, - _recurseList: S[] = [], + _recurseList: S[] = [] ): PathSchema { const firstState = { schema, @@ -61,18 +50,9 @@ function toPathSchemaInternal< while (stack.length > 0) { const { schema, formData, _recurseList, pathSchema, name } = stack.pop()!; - if ( - REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema - ) { - const _schema = retrieveSchema( - validator, - schema, - rootSchema, - formData, - ); - const sameSchemaIndex = _recurseList.findIndex((item) => - isEqual(item, _schema) - ); + if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { + const _schema = retrieveSchema(validator, schema, rootSchema, formData); + const sameSchemaIndex = _recurseList.findIndex((item) => fastDeepEqual(item, _schema)); if (sameSchemaIndex === -1) { stack.push({ schema: _schema, @@ -85,21 +65,12 @@ function toPathSchemaInternal< } } - pathSchema[NAME_KEY] = name.replace(/^\./, ""); + pathSchema[NAME_KEY] = name.replace(/^\./, ''); if (ONE_OF_KEY in schema || ANY_OF_KEY in schema) { - const xxxOf: S[] = ONE_OF_KEY in schema - ? (schema.oneOf as S[]) - : (schema.anyOf as S[]); + const xxxOf: S[] = ONE_OF_KEY in schema ? (schema.oneOf as S[]) : (schema.anyOf as S[]); const discriminator = getDiscriminatorFieldFromSchema(schema); - const index = getClosestMatchingOption( - validator, - rootSchema!, - formData, - xxxOf, - 0, - discriminator, - ); + const index = getClosestMatchingOption(validator, rootSchema!, formData, xxxOf, 0, discriminator); const _schema: S = xxxOf![index] as S; stack.push({ schema: _schema, @@ -110,16 +81,12 @@ function toPathSchemaInternal< }); } - if ( - ADDITIONAL_PROPERTIES_KEY in schema && - schema[ADDITIONAL_PROPERTIES_KEY] !== false - ) { + if (ADDITIONAL_PROPERTIES_KEY in schema && schema[ADDITIONAL_PROPERTIES_KEY] !== false) { set(pathSchema, RJSF_ADDITIONAL_PROPERTIES_FLAG, true); } if (ITEMS_KEY in schema && Array.isArray(formData)) { - const { items: schemaItems, additionalItems: schemaAdditionalItems } = - schema; + const { items: schemaItems, additionalItems: schemaAdditionalItems } = schema; if (Array.isArray(schemaItems)) { formData.forEach((element, i: number) => { @@ -144,9 +111,7 @@ function toPathSchemaInternal< pathSchema: newPathSchema, }); } else { - console.warn( - `Unable to generate path schema for "${name}.${i}". No schema defined for it`, - ); + console.warn(`Unable to generate path schema for "${name}.${i}". No schema defined for it`); } }); } else { @@ -193,16 +158,12 @@ function toPathSchemaInternal< * @param [formData] - The current formData, if any, to assist retrieving a schema * @returns - The `PathSchema` object for the `schema` */ -export default function toPathSchema< - T = any, - S extends StrictRJSFSchema = RJSFSchema, - F extends FormContextType = any, ->( +export default function toPathSchema( validator: ValidatorType, schema: S, - name = "", + name = '', rootSchema?: S, - formData?: T, + formData?: T ): PathSchema { return toPathSchemaInternal(validator, schema, name, rootSchema, formData); } diff --git a/packages/validator-ajv8/src/precompiledValidator.ts b/packages/validator-ajv8/src/precompiledValidator.ts index 3b201b9a90..53f4299ded 100644 --- a/packages/validator-ajv8/src/precompiledValidator.ts +++ b/packages/validator-ajv8/src/precompiledValidator.ts @@ -1,6 +1,6 @@ import { ErrorObject } from 'ajv'; import get from 'lodash/get'; -import isEqual from 'lodash/isEqual'; +import fastDeepEqual from 'fast-deep-equal'; import { CustomValidator, ErrorSchema, @@ -9,13 +9,13 @@ import { hashForSchema, ID_KEY, JUNK_OPTION_ID, + retrieveSchema, RJSFSchema, StrictRJSFSchema, toErrorList, UiSchema, ValidationData, ValidatorType, - retrieveSchema, } from '@rjsf/utils'; import { CompiledValidateFunction, Localizer, ValidatorFunctions } from './types'; @@ -92,10 +92,10 @@ export default class AJV8PrecompiledValidator< * @param [formData] - The form data to validate if any */ ensureSameRootSchema(schema: S, formData?: T) { - if (!isEqual(schema, this.rootSchema)) { + if (!fastDeepEqual(schema, this.rootSchema)) { // Resolve the root schema with the passed in form data since that may affect the resolution const resolvedRootSchema = retrieveSchema(this, this.rootSchema, this.rootSchema, formData); - if (!isEqual(schema, resolvedRootSchema)) { + if (!fastDeepEqual(schema, resolvedRootSchema)) { throw new Error( 'The schema associated with the precompiled validator differs from the rootSchema provided for validation' );