Skip to content

Commit 8ffada0

Browse files
authored
fix(standard-schema): Propertly handle object path segments (#746)
* use provided InferOutput helper * use official util to get dot path, allowing for correct handling of object path segments
1 parent 8ea953c commit 8ffada0

File tree

7 files changed

+76
-6
lines changed

7 files changed

+76
-6
lines changed

bun.lock

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"devDependencies": {
1010
"@sinclair/typebox": "^0.34.15",
1111
"@standard-schema/spec": "^1.0.0",
12+
"@standard-schema/utils": "^0.3.0",
1213
"@testing-library/dom": "^10.4.0",
1314
"@testing-library/jest-dom": "^6.6.3",
1415
"@testing-library/react": "^16.2.0",
@@ -412,6 +413,8 @@
412413

413414
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
414415

416+
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
417+
415418
"@surma/rollup-plugin-off-main-thread": ["@surma/rollup-plugin-off-main-thread@2.2.3", "", { "dependencies": { "ejs": "^3.1.6", "json5": "^2.2.0", "magic-string": "^0.25.0", "string.prototype.matchall": "^4.0.6" } }, "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ=="],
416419

417420
"@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="],

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@
269269
"devDependencies": {
270270
"@sinclair/typebox": "^0.34.15",
271271
"@standard-schema/spec": "^1.0.0",
272+
"@standard-schema/utils": "^0.3.0",
272273
"@testing-library/dom": "^10.4.0",
273274
"@testing-library/jest-dom": "^6.6.3",
274275
"@testing-library/react": "^16.2.0",

standard-schema/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"peerDependencies": {
1414
"react-hook-form": "^7.0.0",
1515
"@standard-schema/spec": "^1.0.0",
16+
"@standard-schema/utils": "^0.3.0",
1617
"@hookform/resolvers": "^2.0.0"
1718
}
1819
}

standard-schema/src/__tests__/__fixtures__/data.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { StandardSchemaV1 } from '@standard-schema/spec';
12
import { Field, InternalFieldName } from 'react-hook-form';
23
import { z } from 'zod';
34

@@ -86,3 +87,25 @@ export const fields: Record<InternalFieldName, Field['_f']> = {
8687
name: 'birthday',
8788
},
8889
};
90+
91+
export const customSchema: StandardSchemaV1<
92+
StandardSchemaV1.InferInput<typeof schema>,
93+
StandardSchemaV1.InferOutput<typeof schema>
94+
> = {
95+
'~standard': {
96+
version: 1,
97+
vendor: 'custom',
98+
validate: () => ({
99+
issues: [
100+
{
101+
path: [{ key: 'username' }],
102+
message: 'Custom error',
103+
},
104+
{
105+
path: [{ key: 'like' }, { key: 0 }, { key: 'id' }],
106+
message: 'Custom error',
107+
},
108+
],
109+
}),
110+
},
111+
};

standard-schema/src/__tests__/__snapshots__/standard-schema.ts.snap

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`standardSchemaResolver > should correctly handle path segments that are objects 1`] = `
4+
{
5+
"errors": {
6+
"like": [
7+
{
8+
"id": {
9+
"message": "Custom error",
10+
"ref": undefined,
11+
"type": "",
12+
},
13+
},
14+
],
15+
"username": {
16+
"message": "Custom error",
17+
"ref": {
18+
"name": "username",
19+
},
20+
"type": "",
21+
},
22+
},
23+
"values": {},
24+
}
25+
`;
26+
327
exports[`standardSchemaResolver > should return a single error from standardSchemaResolver when validation fails 1`] = `
428
{
529
"errors": {

standard-schema/src/__tests__/standard-schema.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { standardSchemaResolver } from '..';
2-
import { fields, invalidData, schema, validData } from './__fixtures__/data';
2+
import {
3+
customSchema,
4+
fields,
5+
invalidData,
6+
schema,
7+
validData,
8+
} from './__fixtures__/data';
39

410
const shouldUseNativeValidation = false;
511

@@ -53,4 +59,16 @@ describe('standardSchemaResolver', () => {
5359
expect(validateSpy).toHaveBeenCalledTimes(1);
5460
expect(result).toMatchSnapshot();
5561
});
62+
it('should correctly handle path segments that are objects', async () => {
63+
const result = await standardSchemaResolver(customSchema)(
64+
validData,
65+
undefined,
66+
{
67+
fields,
68+
shouldUseNativeValidation,
69+
},
70+
);
71+
72+
expect(result).toMatchSnapshot();
73+
});
5674
});

standard-schema/src/standard-schema.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
22
import { StandardSchemaV1 } from '@standard-schema/spec';
3+
import { getDotPath } from '@standard-schema/utils';
34
import { FieldError, FieldValues, Resolver } from 'react-hook-form';
45

56
function parseIssues(
@@ -10,7 +11,7 @@ function parseIssues(
1011

1112
for (let i = 0; i < issues.length; i++) {
1213
const error = issues[i];
13-
const path = error.path?.join('.');
14+
const path = getDotPath(error);
1415

1516
if (path) {
1617
if (!errors[path]) {
@@ -52,15 +53,14 @@ function parseIssues(
5253
* ```
5354
*/
5455
export function standardSchemaResolver<
55-
TFieldValues extends FieldValues,
56-
Schema extends StandardSchemaV1<TFieldValues, any>,
56+
Schema extends StandardSchemaV1<FieldValues>,
5757
>(
5858
schema: Schema,
5959
resolverOptions: {
6060
raw?: boolean;
6161
} = {},
62-
): Resolver<NonNullable<(typeof schema)['~standard']['types']>['output']> {
63-
return async (values: TFieldValues, _, options) => {
62+
): Resolver<StandardSchemaV1.InferOutput<Schema>> {
63+
return async (values, _, options) => {
6464
let result = schema['~standard'].validate(values);
6565
if (result instanceof Promise) {
6666
result = await result;

0 commit comments

Comments
 (0)