Skip to content

Commit a4bb2bf

Browse files
Revert "feat: improve deepEquals performance (#4292)" (#4300)
* Revert "feat: improve deepEquals performance (#4292)" This reverts commit 514ea85. * package.json
1 parent ff94324 commit a4bb2bf

14 files changed

+51
-80
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ should change the heading of the (upcoming) version to include a major version b
1616
1717
-->
1818

19+
# 5.21.1
20+
21+
## @rjsf/utils
22+
23+
- Revert of updating `deepEquals()` from [#4292]
24+
25+
## @validator-ajv8
26+
27+
- Revert of using `deepEquals()` instead of `lodash.isEqual()` from [#4292]
28+
1929
# 5.21.0
2030

2131
## @rjsf/core

package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/utils/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
"react": "^16.14.0 || >=17"
3737
},
3838
"dependencies": {
39-
"fast-equals": "^5.0.1",
4039
"json-schema-merge-allof": "^0.8.1",
4140
"jsonpointer": "^5.0.1",
4241
"lodash": "^4.17.21",

packages/utils/src/createSchemaUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import {
1414
ValidatorType,
1515
} from './types';
1616
import {
17-
getClosestMatchingOption,
1817
getDefaultFormState,
1918
getDisplayLabel,
19+
getClosestMatchingOption,
2020
getFirstMatchingOption,
2121
getMatchingOption,
2222
isFilesArray,

packages/utils/src/deepEquals.ts

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,19 @@
1-
import { createCustomEqual, State } from 'fast-equals';
1+
import isEqualWith from 'lodash/isEqualWith';
22

3-
/** Check if all parameters are typeof function.
4-
*
5-
* @param a - The first element to check typeof
6-
* @param b - The second element to check typeof
7-
* @returns - if typeof a and b are equal to function return true, otherwise false
8-
*/
9-
function isFunctions(a: any, b: any) {
10-
return typeof a === 'function' && typeof b === 'function';
11-
}
12-
13-
/** Implements a deep equals using the `fast-equal.createCustomEqual` function, that provides a customized comparator that
14-
* assumes all functions in objects are equivalent.
15-
*
16-
* @param a - The first element to compare
17-
* @param b - The second element to compare
18-
* @returns - True if the `a` and `b` are deeply equal, false otherwise
19-
*/
20-
const customDeepEqual = createCustomEqual({
21-
createInternalComparator: (comparator: (a: any, b: any, state: State<any>) => boolean) => {
22-
return (a: any, b: any, _idxA: any, _idxB: any, _parentA: any, _parentB: any, state: State<any>) => {
23-
if (isFunctions(a, b)) {
24-
// Assume all functions are equivalent
25-
// see https://github.com/rjsf-team/react-jsonschema-form/issues/255
26-
return true;
27-
}
28-
29-
return comparator(a, b, state);
30-
};
31-
},
32-
});
33-
34-
/** Implements a deep equals using the `fast-equal.createCustomEqual` function, that provides a customized comparator that
3+
/** Implements a deep equals using the `lodash.isEqualWith` function, that provides a customized comparator that
354
* assumes all functions are equivalent.
365
*
376
* @param a - The first element to compare
387
* @param b - The second element to compare
398
* @returns - True if the `a` and `b` are deeply equal, false otherwise
409
*/
4110
export default function deepEquals(a: any, b: any): boolean {
42-
if (isFunctions(a, b)) {
43-
return true;
44-
}
45-
return customDeepEqual(a, b);
11+
return isEqualWith(a, b, (obj: any, other: any) => {
12+
if (typeof obj === 'function' && typeof other === 'function') {
13+
// Assume all functions are equivalent
14+
// see https://github.com/rjsf-team/react-jsonschema-form/issues/255
15+
return true;
16+
}
17+
return undefined; // fallback to default isEquals behavior
18+
});
4619
}

packages/utils/src/enumOptionsDeselectValue.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import isEqual from 'lodash/isEqual';
2+
13
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
24
import enumOptionsValueForIndex from './enumOptionsValueForIndex';
3-
import deepEquals from './deepEquals';
45

56
/** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is
67
* a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is
@@ -21,7 +22,7 @@ export default function enumOptionsDeselectValue<S extends StrictRJSFSchema = RJ
2122
): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined {
2223
const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions);
2324
if (Array.isArray(selected)) {
24-
return selected.filter((v) => !deepEquals(v, value));
25+
return selected.filter((v) => !isEqual(v, value));
2526
}
26-
return deepEquals(value, selected) ? undefined : selected;
27+
return isEqual(value, selected) ? undefined : selected;
2728
}

packages/utils/src/enumOptionsIsSelected.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import deepEquals from './deepEquals';
1+
import isEqual from 'lodash/isEqual';
2+
23
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
34

45
/** Determines whether the given `value` is (one of) the `selected` value(s).
@@ -12,7 +13,7 @@ export default function enumOptionsIsSelected<S extends StrictRJSFSchema = RJSFS
1213
selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][]
1314
) {
1415
if (Array.isArray(selected)) {
15-
return selected.some((sel) => deepEquals(sel, value));
16+
return selected.some((sel) => isEqual(sel, value));
1617
}
17-
return deepEquals(selected, value);
18+
return isEqual(selected, value);
1819
}

packages/utils/src/parser/ParserValidator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import get from 'lodash/get';
2+
import isEqual from 'lodash/isEqual';
23

34
import { ID_KEY } from '../constants';
45
import hashForSchema from '../hashForSchema';
@@ -14,7 +15,6 @@ import {
1415
ValidationData,
1516
ValidatorType,
1617
} from '../types';
17-
import deepEquals from '../deepEquals';
1818

1919
/** The type of the map of schema hash to schema
2020
*/
@@ -67,7 +67,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
6767
const existing = this.schemaMap[key];
6868
if (!existing) {
6969
this.schemaMap[key] = identifiedSchema;
70-
} else if (!deepEquals(existing, identifiedSchema)) {
70+
} else if (!isEqual(existing, identifiedSchema)) {
7171
console.error('existing schema:', JSON.stringify(existing, null, 2));
7272
console.error('new schema:', JSON.stringify(identifiedSchema, null, 2));
7373
throw new Error(
@@ -91,7 +91,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
9191
* @throws - Error when the given `rootSchema` differs from the root schema provided during construction
9292
*/
9393
isValid(schema: S, _formData: T, rootSchema: S): boolean {
94-
if (!deepEquals(rootSchema, this.rootSchema)) {
94+
if (!isEqual(rootSchema, this.rootSchema)) {
9595
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema');
9696
}
9797
this.addSchema(schema, hashForSchema<S>(schema));

packages/utils/src/parser/schemaParser.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import forEach from 'lodash/forEach';
2+
import isEqual from 'lodash/isEqual';
23

34
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types';
4-
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants';
5+
import { PROPERTIES_KEY, ITEMS_KEY } from '../constants';
56
import ParserValidator, { SchemaMap } from './ParserValidator';
6-
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema';
7-
import deepEquals from '../deepEquals';
7+
import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema';
88

99
/** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to
1010
* capture the sub-schemas that the `isValid()` function is called with. For each schema returned by the
@@ -24,7 +24,7 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
2424
) {
2525
const schemas = retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, undefined, true);
2626
schemas.forEach((schema) => {
27-
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema));
27+
const sameSchemaIndex = recurseList.findIndex((item) => isEqual(item, schema));
2828
if (sameSchemaIndex === -1) {
2929
recurseList.push(schema);
3030
const allOptions = resolveAnyOrOneOfSchemas<T, S, F>(validator, schema, rootSchema, true);

packages/utils/src/schema/retrieveSchema.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import get from 'lodash/get';
2+
import isEqual from 'lodash/isEqual';
23
import set from 'lodash/set';
34
import times from 'lodash/times';
45
import transform from 'lodash/transform';
@@ -14,10 +15,10 @@ import {
1415
ANY_OF_KEY,
1516
DEPENDENCIES_KEY,
1617
IF_KEY,
17-
ITEMS_KEY,
1818
ONE_OF_KEY,
19-
PROPERTIES_KEY,
2019
REF_KEY,
20+
PROPERTIES_KEY,
21+
ITEMS_KEY,
2122
} from '../constants';
2223
import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition';
2324
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
@@ -26,7 +27,6 @@ import isObject from '../isObject';
2627
import mergeSchemas from '../mergeSchemas';
2728
import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
2829
import getFirstMatchingOption from './getFirstMatchingOption';
29-
import deepEquals from '../deepEquals';
3030

3131
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
3232
* resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
@@ -196,10 +196,7 @@ export function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema,
196196
)
197197
);
198198
const allPermutations = getAllPermutationsOfXxxOf<S>(allOfSchemaElements);
199-
return allPermutations.map((permutation) => ({
200-
...schema,
201-
allOf: permutation,
202-
}));
199+
return allPermutations.map((permutation) => ({ ...schema, allOf: permutation }));
203200
}
204201
// No $ref or dependencies or allOf attribute was found, returning the original schema.
205202
return [schema];
@@ -296,7 +293,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
296293
};
297294
}
298295

299-
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
296+
return isEqual(schema, resolvedSchema) ? schema : resolvedSchema;
300297
}
301298

302299
/** Creates new 'properties' items for each key in the `formData`

packages/utils/src/schema/toIdSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import get from 'lodash/get';
2+
import isEqual from 'lodash/isEqual';
23

34
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants';
45
import isObject from '../isObject';
56
import { FormContextType, GenericObjectType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
67
import retrieveSchema from './retrieveSchema';
78
import getSchemaType from '../getSchemaType';
8-
import deepEquals from '../deepEquals';
99

1010
/** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
1111
* infinite recursion
@@ -32,7 +32,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
3232
): IdSchema<T> {
3333
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
3434
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
35-
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
35+
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
3636
if (sameSchemaIndex === -1) {
3737
return toIdSchemaInternal<T, S, F>(
3838
validator,

packages/utils/src/schema/toPathSchema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import get from 'lodash/get';
2+
import isEqual from 'lodash/isEqual';
23
import set from 'lodash/set';
34

45
import {
5-
ADDITIONAL_PROPERTIES_KEY,
66
ALL_OF_KEY,
77
ANY_OF_KEY,
8+
ADDITIONAL_PROPERTIES_KEY,
89
DEPENDENCIES_KEY,
910
ITEMS_KEY,
1011
NAME_KEY,
@@ -17,7 +18,6 @@ import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'
1718
import { FormContextType, GenericObjectType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
1819
import getClosestMatchingOption from './getClosestMatchingOption';
1920
import retrieveSchema from './retrieveSchema';
20-
import deepEquals from '../deepEquals';
2121

2222
/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
2323
* infinite recursion
@@ -40,7 +40,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
4040
): PathSchema<T> {
4141
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
4242
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
43-
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
43+
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
4444
if (sameSchemaIndex === -1) {
4545
return toPathSchemaInternal<T, S, F>(
4646
validator,

packages/utils/test/deepEquals.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { deepEquals } from '../src';
22

33
describe('deepEquals()', () => {
4-
// Note: deepEquals implementation uses fast-equal.createCustomEqual, so we focus on the behavioral differences we introduced.
4+
// Note: deepEquals implementation uses isEqualWith, so we focus on the behavioral differences we introduced.
55
it('should assume functions are always equivalent', () => {
66
expect(
77
deepEquals(

packages/validator-ajv8/src/precompiledValidator.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import { ErrorObject } from 'ajv';
22
import get from 'lodash/get';
3+
import isEqual from 'lodash/isEqual';
34
import {
45
CustomValidator,
5-
deepEquals,
66
ErrorSchema,
77
ErrorTransformer,
88
FormContextType,
99
hashForSchema,
1010
ID_KEY,
1111
JUNK_OPTION_ID,
12-
retrieveSchema,
1312
RJSFSchema,
1413
StrictRJSFSchema,
1514
toErrorList,
1615
UiSchema,
1716
ValidationData,
1817
ValidatorType,
18+
retrieveSchema,
1919
} from '@rjsf/utils';
2020

2121
import { CompiledValidateFunction, Localizer, ValidatorFunctions } from './types';
@@ -92,10 +92,10 @@ export default class AJV8PrecompiledValidator<
9292
* @param [formData] - The form data to validate if any
9393
*/
9494
ensureSameRootSchema(schema: S, formData?: T) {
95-
if (!deepEquals(schema, this.rootSchema)) {
95+
if (!isEqual(schema, this.rootSchema)) {
9696
// Resolve the root schema with the passed in form data since that may affect the resolution
9797
const resolvedRootSchema = retrieveSchema(this, this.rootSchema, this.rootSchema, formData);
98-
if (!deepEquals(schema, resolvedRootSchema)) {
98+
if (!isEqual(schema, resolvedRootSchema)) {
9999
throw new Error(
100100
'The schema associated with the precompiled validator differs from the rootSchema provided for validation'
101101
);

0 commit comments

Comments
 (0)