From 46ccadfe7d4b9262a626be533cf7d5692ec8f1de Mon Sep 17 00:00:00 2001 From: Adrian Tedeschi Date: Mon, 3 Jul 2023 07:39:45 -0300 Subject: [PATCH 1/4] fix: performance improvement (#2043) --- package.json | 2 +- src/ValidationError.ts | 14 +++++++++----- src/schema.ts | 34 +++++++++++++++++++++++++++++++--- src/types.ts | 4 ++++ src/util/createValidation.ts | 9 ++++++++- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index cbf3de4ae..0877a747b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yup", - "version": "1.2.0", + "version": "1.2.0-dev6", "description": "Dead simple Object schema validation", "main": "lib/index.js", "module": "lib/index.esm.js", diff --git a/src/ValidationError.ts b/src/ValidationError.ts index f02879ff8..c8367c462 100644 --- a/src/ValidationError.ts +++ b/src/ValidationError.ts @@ -5,7 +5,10 @@ let strReg = /\$\{\s*(\w+)\s*\}/g; type Params = Record; -export default class ValidationError extends Error { +export default class ValidationError implements Error { + name: string; + message: string; + stack?: string | undefined; value: any; path?: string; type?: string; @@ -38,9 +41,8 @@ export default class ValidationError extends Error { value?: any, field?: string, type?: string, + disableStack?: boolean, ) { - super(); - this.name = 'ValidationError'; this.value = value; this.path = field; @@ -52,7 +54,8 @@ export default class ValidationError extends Error { toArray(errorOrErrors).forEach((err) => { if (ValidationError.isError(err)) { this.errors.push(...err.errors); - this.inner = this.inner.concat(err.inner.length ? err.inner : err); + const innerErrors = err.inner.length ? err.inner : [err]; + this.inner.splice(this.inner.length, 0, ...innerErrors); } else { this.errors.push(err); } @@ -63,6 +66,7 @@ export default class ValidationError extends Error { ? `${this.errors.length} errors occurred` : this.errors[0]; - if (Error.captureStackTrace) Error.captureStackTrace(this, ValidationError); + if (!disableStack && Error.captureStackTrace) + Error.captureStackTrace(this, ValidationError); } } diff --git a/src/schema.ts b/src/schema.ts index b8cf53d26..d54bdb1db 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -43,6 +43,7 @@ export type SchemaSpec = { strip?: boolean; strict?: boolean; recursive?: boolean; + disableStackTrace?: boolean; label?: string | undefined; meta?: SchemaMetadata; }; @@ -191,6 +192,7 @@ export default abstract class Schema< strict: false, abortEarly: true, recursive: true, + disableStackTrace: false, nullable: false, optional: true, coerce: true, @@ -345,6 +347,8 @@ export default abstract class Schema< strict: options.strict ?? this.spec.strict, abortEarly: options.abortEarly ?? this.spec.abortEarly, recursive: options.recursive ?? this.spec.recursive, + disableStackTrace: + options.disableStackTrace ?? this.spec.disableStackTrace, }; } @@ -499,7 +503,11 @@ export default abstract class Schema< test(args!, panicOnce, function finishTestRun(err) { if (err) { - nestedErrors = nestedErrors.concat(err); + nestedErrors.splice( + nestedErrors.length, + 0, + ...(Symbol.iterator in err ? err : [err]), + ); } if (--count <= 0) { nextOnce(nestedErrors); @@ -553,6 +561,8 @@ export default abstract class Schema< options?: ValidateOptions, ): Promise { let schema = this.resolve({ ...options, value }); + let { disableStackTrace = this.spec.disableStackTrace } = + options ?? this.spec; return new Promise((resolve, reject) => schema._validate( @@ -563,7 +573,16 @@ export default abstract class Schema< reject(error); }, (errors, validated) => { - if (errors.length) reject(new ValidationError(errors!, validated)); + if (errors.length) + reject( + new ValidationError( + errors!, + validated, + undefined, + undefined, + disableStackTrace, + ), + ); else resolve(validated as this['__outputType']); }, ), @@ -576,6 +595,8 @@ export default abstract class Schema< ): this['__outputType'] { let schema = this.resolve({ ...options, value }); let result: any; + let { disableStackTrace = this.spec.disableStackTrace } = + options ?? this.spec; schema._validate( value, @@ -585,7 +606,14 @@ export default abstract class Schema< throw error; }, (errors, validated) => { - if (errors.length) throw new ValidationError(errors!, value); + if (errors.length) + throw new ValidationError( + errors!, + value, + undefined, + undefined, + disableStackTrace, + ); result = validated; }, ); diff --git a/src/types.ts b/src/types.ts index c63fee563..4a04d59a0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -61,6 +61,10 @@ export interface ValidateOptions { * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true */ recursive?: boolean; + /** + * When true ValidationError instance won't include stack trace information. Default - false + */ + disableStackTrace?: boolean; /** * Any context needed for validating schema conditions (see: when()) */ diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index dc21e48c9..ba543b9c8 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -22,6 +22,7 @@ export type CreateErrorOptions = { message?: Message; params?: ExtraParams; type?: string; + disableStackTrace?: boolean; }; export type TestContext = { @@ -79,7 +80,12 @@ export default function createValidation(config: { next: NextCallback, ) { const { name, test, params, message, skipAbsent } = config; - let { parent, context, abortEarly = schema.spec.abortEarly } = options; + let { + parent, + context, + abortEarly = schema.spec.abortEarly, + disableStackTrace = schema.spec.disableStackTrace, + } = options; function resolve(item: T | Reference) { return Ref.isRef(item) ? item.getValue(value, parent, context) : item; @@ -105,6 +111,7 @@ export default function createValidation(config: { value, nextParams.path, overrides.type || name, + overrides.disableStackTrace ?? disableStackTrace, ); error.params = nextParams; return error; From 554280e14c5ab2735a3a35f9787b700674f65e70 Mon Sep 17 00:00:00 2001 From: tedeschia Date: Fri, 7 Jul 2023 15:03:53 -0300 Subject: [PATCH 2/4] Update src/ValidationError.ts Co-authored-by: Jason Quense --- src/ValidationError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ValidationError.ts b/src/ValidationError.ts index c8367c462..89047b909 100644 --- a/src/ValidationError.ts +++ b/src/ValidationError.ts @@ -55,7 +55,7 @@ export default class ValidationError implements Error { if (ValidationError.isError(err)) { this.errors.push(...err.errors); const innerErrors = err.inner.length ? err.inner : [err]; - this.inner.splice(this.inner.length, 0, ...innerErrors); + this.inner.push(...innerErrors); } else { this.errors.push(err); } From 3e3483737cd990a048788d426caa4afbe6a93b07 Mon Sep 17 00:00:00 2001 From: tedeschia Date: Fri, 7 Jul 2023 15:10:27 -0300 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Jason Quense --- src/schema.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/schema.ts b/src/schema.ts index d54bdb1db..64403890b 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -503,11 +503,7 @@ export default abstract class Schema< test(args!, panicOnce, function finishTestRun(err) { if (err) { - nestedErrors.splice( - nestedErrors.length, - 0, - ...(Symbol.iterator in err ? err : [err]), - ); + Array.isArray(err) ? nestedErrors.push(...err) : nestedErrors.push(err) } if (--count <= 0) { nextOnce(nestedErrors); @@ -561,8 +557,7 @@ export default abstract class Schema< options?: ValidateOptions, ): Promise { let schema = this.resolve({ ...options, value }); - let { disableStackTrace = this.spec.disableStackTrace } = - options ?? this.spec; + let disableStackTrace = options?.disableStackTrace ?? schema.spec.disableStackTrace; return new Promise((resolve, reject) => schema._validate( From 5b12965c49f2c768c78d801f8d2f454aea65653c Mon Sep 17 00:00:00 2001 From: Adrian Tedeschi Date: Fri, 7 Jul 2023 15:13:28 -0300 Subject: [PATCH 4/4] fix PR comments --- README.md | 1 + package.json | 2 +- src/schema.ts | 11 +++++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 86c1df76f..46a404695 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ const parsedUser = await userSchema.validate( + - [Schema basics](#schema-basics) - [Parsing: Transforms](#parsing-transforms) - [Validation: Tests](#validation-tests) diff --git a/package.json b/package.json index 0877a747b..cbf3de4ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yup", - "version": "1.2.0-dev6", + "version": "1.2.0", "description": "Dead simple Object schema validation", "main": "lib/index.js", "module": "lib/index.esm.js", diff --git a/src/schema.ts b/src/schema.ts index 64403890b..b43973d0e 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -503,7 +503,9 @@ export default abstract class Schema< test(args!, panicOnce, function finishTestRun(err) { if (err) { - Array.isArray(err) ? nestedErrors.push(...err) : nestedErrors.push(err) + Array.isArray(err) + ? nestedErrors.push(...err) + : nestedErrors.push(err); } if (--count <= 0) { nextOnce(nestedErrors); @@ -557,7 +559,8 @@ export default abstract class Schema< options?: ValidateOptions, ): Promise { let schema = this.resolve({ ...options, value }); - let disableStackTrace = options?.disableStackTrace ?? schema.spec.disableStackTrace; + let disableStackTrace = + options?.disableStackTrace ?? schema.spec.disableStackTrace; return new Promise((resolve, reject) => schema._validate( @@ -590,8 +593,8 @@ export default abstract class Schema< ): this['__outputType'] { let schema = this.resolve({ ...options, value }); let result: any; - let { disableStackTrace = this.spec.disableStackTrace } = - options ?? this.spec; + let disableStackTrace = + options?.disableStackTrace ?? schema.spec.disableStackTrace; schema._validate( value,