Skip to content

Commit d58001b

Browse files
authored
Merge pull request #55 from x0k/schema-value-deep-equals
Use custom deep equals functions
2 parents 075b3c1 + ffc77e7 commit d58001b

File tree

12 files changed

+110
-36
lines changed

12 files changed

+110
-36
lines changed

.changeset/early-mails-itch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sjsf/form": minor
3+
---
4+
5+
Add `isSchemaValueDeepEqual` and `isSchemaDeepEqual` functions

.changeset/fast-icons-protect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sjsf/ajv8-validator": patch
3+
---
4+
5+
Migrate to `isSchemaDeepEqual`

packages/ajv8-validator/src/validator.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { Ajv, type AsyncValidateFunction, type ValidateFunction } from "ajv";
22
import type { ErrorObject, AnySchema } from "ajv";
3+
import type { AnyValidateFunction } from "ajv/dist/core.js";
34

4-
import type { MaybePromise } from '@sjsf/form/lib/types';
5-
import { deepEqual } from "@sjsf/form/lib/deep-equal";
5+
import type { MaybePromise } from "@sjsf/form/lib/types";
66
import { getValueByPath } from "@sjsf/form/lib/object";
77
import { weakMemoize } from "@sjsf/form/lib/memoize";
88
import {
99
ID_KEY,
1010
prefixSchemaRefs,
1111
ROOT_SCHEMA_PREFIX,
12+
isSchemaDeepEqual,
1213
type Schema,
1314
type SchemaDefinition,
1415
type SchemaValue,
@@ -25,7 +26,6 @@ import {
2526
type UiSchema,
2627
type UiSchemaRoot,
2728
} from "@sjsf/form";
28-
import type { AnyValidateFunction } from "ajv/dist/core.js";
2929

3030
const trueSchema: Schema = {};
3131
const falseSchema: Schema = {
@@ -58,9 +58,12 @@ export function makeSchemaCompiler<A extends boolean>(ajv: Ajv, _async: A) {
5858
return (schema: Schema, rootSchema: Schema) => {
5959
rootSchemaId = rootSchema[ID_KEY] ?? ROOT_SCHEMA_PREFIX;
6060
let ajvSchema = ajv.getSchema(rootSchemaId)?.schema;
61-
// @deprecated
62-
// TODO: Replace deep equality comparison with reference equality by default
63-
if (ajvSchema !== undefined && !deepEqual(ajvSchema, rootSchema)) {
61+
if (
62+
ajvSchema !== undefined &&
63+
// @deprecated
64+
// TODO: Replace deep equality comparison with reference equality by default
65+
!isSchemaDeepEqual(ajvSchema as Schema, rootSchema)
66+
) {
6467
ajv.removeSchema(rootSchemaId);
6568
validatorsCache.delete(schema);
6669
ajvSchema = undefined;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { isObject } from "@/lib/object.js";
2+
3+
import type { Schema, SchemaValue } from "./schema.js";
4+
5+
export function isSchemaValueDeepEqual(
6+
a: SchemaValue | undefined,
7+
b: SchemaValue | undefined
8+
): boolean {
9+
if (a === b) {
10+
return true;
11+
}
12+
if (isObject(a) && isObject(b)) {
13+
if (Array.isArray(a)) {
14+
if (!Array.isArray(b)) {
15+
return false;
16+
}
17+
const { length } = a;
18+
if (length !== b.length) {
19+
return false;
20+
}
21+
for (let i = length; i-- !== 0; ) {
22+
if (!isSchemaValueDeepEqual(a[i], b[i])) {
23+
return false;
24+
}
25+
}
26+
return true;
27+
}
28+
if (Array.isArray(b)) {
29+
return false;
30+
}
31+
const aKeys = Object.keys(a);
32+
let key;
33+
for (let i = aKeys.length; i-- !== 0; ) {
34+
key = aKeys[i]!;
35+
if (!isSchemaValueDeepEqual(a[key], b[key])) {
36+
return false;
37+
}
38+
}
39+
return Object.keys(b).length === aKeys.length;
40+
}
41+
return a !== a && b !== b
42+
}
43+
44+
export const isSchemaDeepEqual = isSchemaValueDeepEqual as (
45+
a: Schema | undefined,
46+
b: Schema | undefined
47+
) => boolean;

packages/form/src/core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ export * from "./merger.js";
2222
export * from "./default-merger.js";
2323
export * from "./schema-traverser.js";
2424
export * from "./schema-value-traverser.js";
25-
export * from './schema-transformer.js';
25+
export * from "./schema-transformer.js";
2626
export * from "./path.js";
27+
export * from "./deep-equal.js";

packages/form/src/core/path-schema.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Licensed under the Apache License, Version 2.0.
33
// Modifications made by Roman Krasilnikov.
44

5-
import { deepEqual } from "@/lib/deep-equal.js";
6-
75
import { retrieveSchema2 } from "./resolve.js";
86
import {
97
ALL_OF_KEY,
@@ -22,6 +20,7 @@ import { isSchemaObjectValue } from "./value.js";
2220
import type { Merger2 } from './merger.js';
2321
import { defaultMerger } from './merger.js';
2422
import { getClosestMatchingOption2 } from './matching.js';
23+
import { isSchemaDeepEqual } from './deep-equal.js';
2524

2625
export const SJSF_ADDITIONAL_PROPERTIES_FLAG = "__sjsf_additionalProperties";
2726

@@ -88,7 +87,7 @@ function toPathSchemaInternal(
8887
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
8988
const _schema = retrieveSchema2(validator, merger, schema, rootSchema, formData);
9089
const sameSchemaIndex = _recurseList.findIndex((item) =>
91-
deepEqual(item, _schema)
90+
isSchemaDeepEqual(item, _schema)
9291
);
9392
if (sameSchemaIndex === -1) {
9493
return toPathSchemaInternal(

packages/form/src/core/resolve.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache License, Version 2.0.
33
// Modifications made by Roman Krasilnikov.
44

5-
import { deepEqual } from "@/lib/deep-equal.js";
65
import { array } from "@/lib/array.js";
76

87
import {
@@ -30,6 +29,7 @@ import { getDiscriminatorFieldFromSchema } from "./discriminator.js";
3029
import { getFirstMatchingOption } from "./matching.js";
3130
import { isSchemaObjectValue } from "./value.js";
3231
import { defaultMerger, type Merger2 } from "./merger.js";
32+
import { isSchemaDeepEqual } from './deep-equal.js';
3333

3434
/**
3535
* @deprecated use `retrieveSchema2`
@@ -157,7 +157,7 @@ export function resolveReference2(
157157
formData?: SchemaValue
158158
): Schema[] {
159159
const resolvedSchema = resolveAllReferences(schema, rootSchema, stack);
160-
if (!deepEqual(schema, resolvedSchema)) {
160+
if (!isSchemaDeepEqual(schema, resolvedSchema)) {
161161
return retrieveSchemaInternal(
162162
validator,
163163
merger,

packages/form/src/form/fields/multi-field.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<script lang="ts">
22
import { proxy } from "@/lib/svelte.svelte";
3-
import { deepEqual } from '@/lib/deep-equal.js'
43
import {
54
getDiscriminatorFieldFromSchema,
5+
isSchemaValueDeepEqual,
66
mergeSchemas,
77
type EnumOption,
88
type SchemaValue,
@@ -53,7 +53,7 @@
5353
retrievedOptions;
5454
return -1;
5555
}
56-
if (currentSelected !== undefined && deepEqual(lastValue, value)) {
56+
if (currentSelected !== undefined && isSchemaValueDeepEqual(lastValue, value)) {
5757
return currentSelected
5858
}
5959
lastValue = $state.snapshot(value)

packages/form/src/form/fields/object/object-field.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script lang="ts">
22
import { untrack } from "svelte";
33
4-
import { deepEqual } from "@/lib/deep-equal.js";
54
import {
65
getDefaultValueForType,
76
getSimpleSchemaType,
87
isAdditionalProperty,
8+
isSchemaDeepEqual,
99
isSchemaExpandable,
1010
isSchemaObjectValue,
1111
orderProperties,
@@ -94,7 +94,7 @@
9494
9595
let lastSchemaProperties: Schema["properties"] = undefined;
9696
const schemaProperties = $derived.by(() => {
97-
if (!deepEqual(lastSchemaProperties, retrievedSchema.properties)) {
97+
if (!isSchemaDeepEqual(lastSchemaProperties, retrievedSchema.properties)) {
9898
lastSchemaProperties = $state.snapshot(retrievedSchema.properties);
9999
}
100100
return lastSchemaProperties;

packages/form/src/form/id-schema.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Licensed under the Apache License, Version 2.0.
33
// Modifications made by Roman Krasilnikov.
44

5-
import { deepEqual } from "@/lib/deep-equal.js";
6-
75
import {
86
ALL_OF_KEY,
97
defaultMerger,
@@ -12,6 +10,7 @@ import {
1210
ID_KEY,
1311
isNormalArrayItems,
1412
isSchema,
13+
isSchemaDeepEqual,
1514
isSchemaObjectValue,
1615
ITEMS_KEY,
1716
PROPERTIES_KEY,
@@ -93,7 +92,7 @@ export function toIdSchema2(
9392
formData
9493
);
9594
const sameSchemaIndex = _recurseList.findIndex((item) =>
96-
deepEqual(item, _schema)
95+
isSchemaDeepEqual(item, _schema)
9796
);
9897
if (sameSchemaIndex === -1) {
9998
return toIdSchema2(

0 commit comments

Comments
 (0)