Skip to content

Commit ef53030

Browse files
author
jquense
committed
feat: resolve ref params if present when describing
fixes #2276
1 parent d00abc3 commit ef53030

File tree

4 files changed

+92
-24
lines changed

4 files changed

+92
-24
lines changed

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import defaultLocale from './locale';
2121
import Schema, {
2222
AnySchema,
2323
CastOptions as BaseCastOptions,
24+
SchemaSpec,
2425
SchemaRefDescription,
2526
SchemaInnerTypeDescription,
2627
SchemaObjectDescription,
@@ -77,6 +78,7 @@ export type {
7778
AnySchema,
7879
MixedOptions,
7980
TypeGuard as MixedTypeGuard,
81+
SchemaSpec,
8082
SchemaRefDescription,
8183
SchemaInnerTypeDescription,
8284
SchemaObjectDescription,

src/schema.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import createValidation, {
1111
NextCallback,
1212
PanicCallback,
1313
TestOptions,
14+
resolveParams,
1415
} from './util/createValidation';
1516
import printValue from './util/printValue';
1617
import Ref from './Reference';
@@ -943,10 +944,21 @@ export default abstract class Schema<
943944
oneOf: next._whitelist.describe(),
944945
notOneOf: next._blacklist.describe(),
945946
tests: next.tests
946-
.map((fn) => ({ name: fn.OPTIONS!.name, params: fn.OPTIONS!.params }))
947947
.filter(
948-
(n, idx, list) => list.findIndex((c) => c.name === n.name) === idx,
949-
),
948+
(n, idx, list) =>
949+
list.findIndex((c) => c.OPTIONS!.name === n.OPTIONS!.name) === idx,
950+
)
951+
.map((fn) => {
952+
const params =
953+
fn.OPTIONS!.params && options
954+
? resolveParams({ ...fn.OPTIONS!.params }, options)
955+
: fn.OPTIONS!.params;
956+
957+
return {
958+
name: fn.OPTIONS!.name,
959+
params,
960+
};
961+
}),
950962
};
951963

952964
return description;

src/util/createValidation.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import Reference from '../Reference';
1111
import type { AnySchema } from '../schema';
1212
import isAbsent from './isAbsent';
13+
import { ResolveOptions } from '../Condition';
1314

1415
export type PanicCallback = (err: Error) => void;
1516

@@ -86,26 +87,21 @@ export default function createValidation(config: {
8687
abortEarly = schema.spec.abortEarly,
8788
disableStackTrace = schema.spec.disableStackTrace,
8889
} = options;
89-
90-
function resolve<T>(item: T | Reference<T>) {
91-
return Ref.isRef(item) ? item.getValue(value, parent, context) : item;
92-
}
93-
90+
const resolveOptions = { value, parent, context };
9491
function createError(overrides: CreateErrorOptions = {}) {
95-
const nextParams = {
96-
value,
97-
originalValue,
98-
label: schema.spec.label,
99-
path: overrides.path || path,
100-
spec: schema.spec,
101-
disableStackTrace: overrides.disableStackTrace || disableStackTrace,
102-
...params,
103-
...overrides.params,
104-
};
105-
106-
type Keys = (keyof typeof nextParams)[];
107-
for (const key of Object.keys(nextParams) as Keys)
108-
nextParams[key] = resolve(nextParams[key]);
92+
const nextParams = resolveParams(
93+
{
94+
value,
95+
originalValue,
96+
label: schema.spec.label,
97+
path: overrides.path || path,
98+
spec: schema.spec,
99+
disableStackTrace: overrides.disableStackTrace || disableStackTrace,
100+
...params,
101+
...overrides.params,
102+
},
103+
resolveOptions,
104+
);
109105

110106
const error = new ValidationError(
111107
ValidationError.formatError(overrides.message || message, nextParams),
@@ -126,7 +122,9 @@ export default function createValidation(config: {
126122
type: name,
127123
from: options.from,
128124
createError,
129-
resolve,
125+
resolve<T>(item: T | Reference<T>) {
126+
return resolveMaybeRef(item, resolveOptions);
127+
},
130128
options,
131129
originalValue,
132130
schema,
@@ -173,3 +171,24 @@ export default function createValidation(config: {
173171

174172
return validate;
175173
}
174+
175+
// Warning: mutates the input
176+
export function resolveParams<T extends ExtraParams>(
177+
params: T,
178+
options: ResolveOptions,
179+
) {
180+
if (!params) return params;
181+
182+
type Keys = (keyof typeof params)[];
183+
for (const key of Object.keys(params) as Keys) {
184+
params[key] = resolveMaybeRef(params[key], options);
185+
}
186+
187+
return params;
188+
}
189+
190+
function resolveMaybeRef<T>(item: T | Reference<T>, options: ResolveOptions) {
191+
return Ref.isRef(item)
192+
? item.getValue(options.value, options.parent, options.context)
193+
: item;
194+
}

test/number.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as TestHelpers from './helpers';
22

3-
import { number, NumberSchema } from '../src';
3+
import { number, NumberSchema, object, ref } from '../src';
44

55
describe('Number types', function () {
66
it('is extensible', () => {
@@ -198,4 +198,39 @@ describe('Number types', function () {
198198
),
199199
]);
200200
});
201+
202+
it('should resolve param refs when describing', () => {
203+
let schema = number().min(ref('$foo'));
204+
205+
expect(schema.describe({ value: 10, context: { foo: 5 } })).toEqual(
206+
expect.objectContaining({
207+
tests: [
208+
expect.objectContaining({
209+
params: {
210+
min: 5,
211+
},
212+
}),
213+
],
214+
}),
215+
);
216+
217+
let schema2 = object({
218+
x: number().min(0),
219+
y: number().min(ref('x')),
220+
}).required();
221+
222+
expect(
223+
schema2.describe({ value: { x: 10 }, context: { foo: 5 } }).fields.y,
224+
).toEqual(
225+
expect.objectContaining({
226+
tests: [
227+
expect.objectContaining({
228+
params: {
229+
min: 10,
230+
},
231+
}),
232+
],
233+
}),
234+
);
235+
});
201236
});

0 commit comments

Comments
 (0)