Skip to content

Commit

Permalink
Change fast-deep-equal to fast-equals
Browse files Browse the repository at this point in the history
  • Loading branch information
igorbrasileiro committed Sep 9, 2024
1 parent be3b6b4 commit a9b2fd6
Show file tree
Hide file tree
Showing 16 changed files with 74 additions and 49 deletions.
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,8 @@
"packages/validator-ajv8",
"packages/snapshot-tests"
],
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"dependencies": {
"fast-equals": "^5.0.1"
}
}
11 changes: 5 additions & 6 deletions packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ 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';

Expand Down Expand Up @@ -283,7 +282,7 @@ export default class Form<
}

this.state = this.getStateFromProps(props, props.formData);
if (this.props.onChange && !fastDeepEqual(this.state.formData, this.props.formData)) {
if (this.props.onChange && !deepEquals(this.state.formData, this.props.formData)) {
this.props.onChange(this.state);
}
this.formElement = createRef();
Expand Down Expand Up @@ -316,8 +315,8 @@ export default class Form<
shouldUpdate: false;
} {
if (!deepEquals(this.props, prevProps)) {
const isSchemaChanged = !fastDeepEqual(prevProps.schema, this.props.schema);
const isFormDataChanged = !fastDeepEqual(prevProps.formData, this.props.formData);
const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);
const isFormDataChanged = !deepEquals(prevProps.formData, this.props.formData);
const nextState = this.getStateFromProps(
this.props,
this.props.formData,
Expand Down Expand Up @@ -361,8 +360,8 @@ export default class Form<
const { nextState } = snapshot;

if (
!fastDeepEqual(nextState.formData, this.props.formData) &&
!fastDeepEqual(nextState.formData, prevState.formData) &&
!deepEquals(nextState.formData, this.props.formData) &&
!deepEquals(nextState.formData, prevState.formData) &&
this.props.onChange
) {
this.props.onChange(nextState);
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/components/fields/MultiSchemaField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import isEmpty from 'lodash/isEmpty';
import omit from 'lodash/omit';
import {
ANY_OF_KEY,
deepEquals,
ERRORS_KEY,
FieldProps,
FormContextType,
Expand All @@ -17,7 +18,6 @@ import {
TranslatableString,
UiSchema,
} from '@rjsf/utils';
import fastDeepEqual from 'fast-deep-equal';

/** Type used for the state of the `AnyOfField` component */
type AnyOfFieldState<S extends StrictRJSFSchema = RJSFSchema> = {
Expand Down Expand Up @@ -67,15 +67,15 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
const { formData, options, idSchema } = this.props;
const { selectedOption } = this.state;
let newState = this.state;
if (!fastDeepEqual(prevProps.options, options)) {
if (!deepEquals(prevProps.options, options)) {
const {
registry: { schemaUtils },
} = this.props;
// re-cache the retrieved options in state in case they have $refs to save doing it later
const retrievedOptions = options.map((opt: S) => schemaUtils.retrieveSchema(opt, formData));
newState = { selectedOption, retrievedOptions };
}
if (!fastDeepEqual(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) {
if (!deepEquals(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) {
const { retrievedOptions } = newState;
const matchingOption = this.getMatchingOption(selectedOption, formData, retrievedOptions);

Expand Down
7 changes: 3 additions & 4 deletions packages/utils/src/createSchemaUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import fastDeepEqual from 'fast-deep-equal';

import {
ErrorSchema,
Experimental_DefaultFormStateBehavior,
Expand Down Expand Up @@ -29,6 +27,7 @@ import {
toIdSchema,
toPathSchema,
} from './schema';
import deepEquals from './deepEquals';

/** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such
* that one does not have to explicitly pass the `validator`, `rootSchema`, or `experimental_defaultFormStateBehavior` to each method.
Expand Down Expand Up @@ -85,8 +84,8 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
}
return (
this.validator !== validator ||
!fastDeepEqual(this.rootSchema, rootSchema) ||
!fastDeepEqual(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior)
!deepEquals(this.rootSchema, rootSchema) ||
!deepEquals(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior)
);
}

Expand Down
32 changes: 23 additions & 9 deletions packages/utils/src/deepEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import isEqualWith from 'lodash/isEqualWith';
import { createCustomEqual, State } from 'fast-equals';

function isFunctions(a: any, b: any) {
return typeof a === 'function' && typeof b === 'function';
}

const customDeepEqual = createCustomEqual({
createInternalComparator: (comparator: (a: any, b: any, state: State<any>) => boolean) => {
return (a: any, b: any, _idxA: any, _idxB: any, _parentA: any, _parentB: any, state: State<any>) => {
if (isFunctions(a, b)) {
// Assume all functions are equivalent
// see https://github.com/rjsf-team/react-jsonschema-form/issues/255
return true;
}

return comparator(a, b, state);
};
},
});

/** Implements a deep equals using the `lodash.isEqualWith` function, that provides a customized comparator that
* assumes all functions are equivalent.
Expand All @@ -8,12 +26,8 @@ import isEqualWith from 'lodash/isEqualWith';
* @returns - True if the `a` and `b` are deeply equal, false otherwise
*/
export default function deepEquals(a: any, b: any): boolean {
return isEqualWith(a, b, (obj: any, other: any) => {
if (typeof obj === 'function' && typeof other === 'function') {
// Assume all functions are equivalent
// see https://github.com/rjsf-team/react-jsonschema-form/issues/255
return true;
}
return undefined; // fallback to default isEquals behavior
});
if (isFunctions(a, b)) {
return true;
}
return customDeepEqual(a, b);
}
7 changes: 3 additions & 4 deletions packages/utils/src/enumOptionsDeselectValue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fastDeepEqual from 'fast-deep-equal';

import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
import enumOptionsValueForIndex from './enumOptionsValueForIndex';
import deepEquals from './deepEquals';

/** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is
* a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is
Expand All @@ -22,7 +21,7 @@ export default function enumOptionsDeselectValue<S extends StrictRJSFSchema = RJ
): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined {
const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions);
if (Array.isArray(selected)) {
return selected.filter((v) => !fastDeepEqual(v, value));
return selected.filter((v) => !deepEquals(v, value));
}
return fastDeepEqual(value, selected) ? undefined : selected;
return deepEquals(value, selected) ? undefined : selected;
}
7 changes: 3 additions & 4 deletions packages/utils/src/enumOptionsIsSelected.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import fastDeepEqual from 'fast-deep-equal';

import deepEquals from './deepEquals';
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';

/** Determines whether the given `value` is (one of) the `selected` value(s).
Expand All @@ -13,7 +12,7 @@ export default function enumOptionsIsSelected<S extends StrictRJSFSchema = RJSFS
selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][]
) {
if (Array.isArray(selected)) {
return selected.some((sel) => fastDeepEqual(sel, value));
return selected.some((sel) => deepEquals(sel, value));
}
return fastDeepEqual(selected, value);
return deepEquals(selected, value);
}
6 changes: 3 additions & 3 deletions packages/utils/src/parser/ParserValidator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import get from 'lodash/get';
import fastDeepEqual from 'fast-deep-equal';

import { ID_KEY } from '../constants';
import hashForSchema from '../hashForSchema';
Expand All @@ -15,6 +14,7 @@ import {
ValidationData,
ValidatorType,
} from '../types';
import deepEquals from '../deepEquals';

/** The type of the map of schema hash to schema
*/
Expand Down Expand Up @@ -67,7 +67,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
const existing = this.schemaMap[key];
if (!existing) {
this.schemaMap[key] = identifiedSchema;
} else if (!fastDeepEqual(existing, identifiedSchema)) {
} else if (!deepEquals(existing, identifiedSchema)) {
console.error('existing schema:', JSON.stringify(existing, null, 2));
console.error('new schema:', JSON.stringify(identifiedSchema, null, 2));
throw new Error(
Expand All @@ -91,7 +91,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
* @throws - Error when the given `rootSchema` differs from the root schema provided during construction
*/
isValid(schema: S, _formData: T, rootSchema: S): boolean {
if (!fastDeepEqual(rootSchema, this.rootSchema)) {
if (!deepEquals(rootSchema, this.rootSchema)) {
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema');
}
this.addSchema(schema, hashForSchema<S>(schema));
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/parser/schemaParser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import forEach from 'lodash/forEach';
import fastDeepEqual from 'fast-deep-equal';

import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types';
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants';
import ParserValidator, { SchemaMap } from './ParserValidator';
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema';
import deepEquals from '../deepEquals';

/** 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
Expand All @@ -24,7 +24,7 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
) {
const schemas = retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, undefined, true);
schemas.forEach((schema) => {
const sameSchemaIndex = recurseList.findIndex((item) => fastDeepEqual(item, schema));
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema));
if (sameSchemaIndex === -1) {
recurseList.push(schema);
const allOptions = resolveAnyOrOneOfSchemas<T, S, F>(validator, schema, rootSchema, true);
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/schema/retrieveSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import get from 'lodash/get';
import fastDeepEqual from 'fast-deep-equal';
import set from 'lodash/set';
import times from 'lodash/times';
import transform from 'lodash/transform';
Expand Down Expand Up @@ -27,6 +26,7 @@ import isObject from '../isObject';
import mergeSchemas from '../mergeSchemas';
import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
import getFirstMatchingOption from './getFirstMatchingOption';
import deepEquals from '../deepEquals';

/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
* resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
Expand Down Expand Up @@ -296,7 +296,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
};
}

return fastDeepEqual(schema, resolvedSchema) ? schema : resolvedSchema;
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
}

/** Creates new 'properties' items for each key in the `formData`
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/schema/toIdSchema.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import get from 'lodash/get';
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';
import { FormContextType, GenericObjectType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
import retrieveSchema from './retrieveSchema';
import getSchemaType from '../getSchemaType';
import deepEquals from '../deepEquals';

/** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
* infinite recursion
Expand All @@ -32,7 +32,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
): IdSchema<T> {
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
const sameSchemaIndex = _recurseList.findIndex((item) => fastDeepEqual(item, _schema));
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
if (sameSchemaIndex === -1) {
return toIdSchemaInternal<T, S, F>(
validator,
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/schema/toPathSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import get from 'lodash/get';
import fastDeepEqual from 'fast-deep-equal';
import set from 'lodash/set';

import {
Expand All @@ -18,6 +17,7 @@ import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'
import { FormContextType, GenericObjectType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
import getClosestMatchingOption from './getClosestMatchingOption';
import retrieveSchema from './retrieveSchema';
import deepEquals from '../deepEquals';

/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
* infinite recursion
Expand All @@ -40,7 +40,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
): PathSchema<T> {
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
const sameSchemaIndex = _recurseList.findIndex((item) => fastDeepEqual(item, _schema));
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
if (sameSchemaIndex === -1) {
return toPathSchemaInternal<T, S, F>(
validator,
Expand Down
4 changes: 2 additions & 2 deletions packages/validator-ajv6/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Ajv, ErrorObject } from 'ajv';
import {
createErrorHandler,
CustomValidator,
deepEquals,
ErrorSchema,
ErrorTransformer,
FormContextType,
Expand All @@ -19,7 +20,6 @@ import {
ValidatorType,
withIdRefPrefix,
} from '@rjsf/utils';
import fastDeepEqual from 'fast-deep-equal';

import { CustomValidatorOptionsType } from './types';
import createAjvInstance from './createAjvInstance';
Expand Down Expand Up @@ -170,7 +170,7 @@ export default class AJV6Validator<T = any, S extends StrictRJSFSchema = RJSFSch
// else 'handleRootSchemaChange' should be called if the root schema changes so we don't have to remove and recompile the schema every run.
if (this.ajv.getSchema(ROOT_SCHEMA_PREFIX) === undefined) {
this.ajv.addSchema(rootSchema, ROOT_SCHEMA_PREFIX);
} else if (!fastDeepEqual(rootSchema, this.ajv.getSchema(ROOT_SCHEMA_PREFIX)?.schema)) {
} else if (!deepEquals(rootSchema, this.ajv.getSchema(ROOT_SCHEMA_PREFIX)?.schema)) {
this.ajv.removeSchema(rootSchemaId);
this.ajv.addSchema(rootSchema, rootSchemaId);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/validator-ajv8/src/precompiledValidator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ErrorObject } from 'ajv';
import get from 'lodash/get';
import fastDeepEqual from 'fast-deep-equal';
import {
CustomValidator,
deepEquals,
ErrorSchema,
ErrorTransformer,
FormContextType,
Expand Down Expand Up @@ -92,10 +92,10 @@ export default class AJV8PrecompiledValidator<
* @param [formData] - The form data to validate if any
*/
ensureSameRootSchema(schema: S, formData?: T) {
if (!fastDeepEqual(schema, this.rootSchema)) {
if (!deepEquals(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 (!fastDeepEqual(schema, resolvedRootSchema)) {
if (!deepEquals(schema, resolvedRootSchema)) {
throw new Error(
'The schema associated with the precompiled validator differs from the rootSchema provided for validation'
);
Expand Down
Loading

0 comments on commit a9b2fd6

Please sign in to comment.