diff --git a/README.md b/README.md index cde01b1..9f84bf1 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,36 @@ class Plugin { export default Plugin; ``` +### Allow to disable and enable validation (the `validate` function do nothing) + +This can be useful when you don't want to do validation for `production` builds. + +```js +import { disableValidation, enableValidation, validate } from "schema-utils"; + +// Disable validation +disableValidation(); +// Do nothing +validate(schema, options); + +// Enable validation +enableValidation(); +// Will throw an error if schema is not valid +validate(schema, options); + +// Allow to undestand do you need validation or not +const need = needValidate(); + +console.log(need); +``` + +Also you can enable/disable validation using the `process.env.SKIP_VALIDATION` env variable. + +Supported values (case insensitive): + +- `yes`/`y`/`true`/`1`/`on` +- `no`/`n`/`false`/`0`/`off` + ## Contributing Please take a moment to read our contributing guidelines if you haven't yet done so. diff --git a/declarations/index.d.ts b/declarations/index.d.ts index df5ec27..def01ba 100644 --- a/declarations/index.d.ts +++ b/declarations/index.d.ts @@ -1,3 +1,12 @@ import { validate } from "./validate"; import { ValidationError } from "./validate"; -export { validate, ValidationError }; +import { enableValidation } from "./validate"; +import { disableValidation } from "./validate"; +import { needValidate } from "./validate"; +export { + validate, + ValidationError, + enableValidation, + disableValidation, + needValidate, +}; diff --git a/declarations/validate.d.ts b/declarations/validate.d.ts index 11fb927..fc5d2ff 100644 --- a/declarations/validate.d.ts +++ b/declarations/validate.d.ts @@ -34,5 +34,8 @@ export function validate( options: Array | object, configuration?: ValidationErrorConfiguration | undefined ): void; +export function enableValidation(): void; +export function disableValidation(): void; +export function needValidate(): boolean; import ValidationError from "./ValidationError"; export { ValidationError }; diff --git a/src/index.js b/src/index.js index 66ebf9d..1166d6f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,15 @@ -const { validate, ValidationError } = require("./validate"); +const { + validate, + ValidationError, + enableValidation, + disableValidation, + needValidate, +} = require("./validate"); -module.exports = { validate, ValidationError }; +module.exports = { + validate, + ValidationError, + enableValidation, + disableValidation, + needValidate, +}; diff --git a/src/validate.js b/src/validate.js index b2bd48c..8b460f0 100644 --- a/src/validate.js +++ b/src/validate.js @@ -55,7 +55,7 @@ const getAjv = memoize(() => { /** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */ -/** @typedef {ErrorObject & { children?: Array}} SchemaUtilErrorObject */ +/** @typedef {ErrorObject & { children?: Array }} SchemaUtilErrorObject */ /** * @callback PostFormatter @@ -87,6 +87,51 @@ function applyPrefix(error, idx) { return error; } +let skipValidation = false; + +// We use `process.env.SKIP_VALIDATION` because you can have multiple `schema-utils` with different version, +// so we want to disable it globally, `process.env` doesn't supported by browsers, so we have the local `skipValidation` variables + +// Enable validation +function enableValidation() { + skipValidation = false; + + // Disable validation for any versions + if (process && process.env) { + process.env.SKIP_VALIDATION = "n"; + } +} + +// Disable validation +function disableValidation() { + skipValidation = true; + + if (process && process.env) { + process.env.SKIP_VALIDATION = "y"; + } +} + +// Check if we need to confirm +function needValidate() { + if (skipValidation) { + return false; + } + + if (process && process.env && process.env.SKIP_VALIDATION) { + const value = process.env.SKIP_VALIDATION.trim(); + + if (/^(?:y|yes|true|1|on)$/i.test(value)) { + return false; + } + + if (/^(?:n|no|false|0|off)$/i.test(value)) { + return true; + } + } + + return true; +} + /** * @param {Schema} schema * @param {Array | object} options @@ -94,6 +139,10 @@ function applyPrefix(error, idx) { * @returns {void} */ function validate(schema, options, configuration) { + if (!needValidate()) { + return; + } + let errors = []; if (Array.isArray(options)) { @@ -165,4 +214,10 @@ function filterErrors(errors) { return newErrors; } -export { validate, ValidationError }; +export { + validate, + enableValidation, + disableValidation, + needValidate, + ValidationError, +}; diff --git a/test/__snapshots__/api.test.js.snap b/test/__snapshots__/api.test.js.snap index e3fb769..a93ce6e 100644 --- a/test/__snapshots__/api.test.js.snap +++ b/test/__snapshots__/api.test.js.snap @@ -1,5 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`api should allow to enable validation using "process.env.SKIP_VALIDATION" #2 1`] = ` +"Invalid options object. NAME has been initialized using an options object that does not match the API schema. + - options has an unknown property 'foo'. These properties are valid: + object { name? }" +`; + +exports[`api should allow to enable validation using "process.env.SKIP_VALIDATION" 1`] = ` +"Invalid options object. NAME has been initialized using an options object that does not match the API schema. + - options has an unknown property 'foo'. These properties are valid: + object { name? }" +`; + +exports[`api should allow to enable validation using API 1`] = ` +"Invalid options object. NAME has been initialized using an options object that does not match the API schema. + - options has an unknown property 'foo'. These properties are valid: + object { name? }" +`; + exports[`api should get configuration from schema 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'foo'. These properties are valid: diff --git a/test/api.test.js b/test/api.test.js index 445f0b9..e75bc6b 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,4 +1,10 @@ -import { validate, ValidationError } from "../src/index"; +import { + validate, + ValidationError, + enableValidation, + disableValidation, + needValidate, +} from "../src/index"; import schema from "./fixtures/schema.json"; import schemaTitle from "./fixtures/schema-title.json"; @@ -196,4 +202,133 @@ describe("api", () => { expect(error.message).toMatchSnapshot(); } }); + + it('should allow to disable validation using "process.env.SKIP_VALIDATION"', () => { + const oldValue = process.env.SKIP_VALIDATION; + + let errored; + + process.env.SKIP_VALIDATION = "y"; + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + errored = error; + } + + expect(errored).toBeUndefined(); + + process.env.SKIP_VALIDATION = oldValue; + }); + + it('should allow to disable validation using "process.env.SKIP_VALIDATION" #2', () => { + const oldValue = process.env.SKIP_VALIDATION; + + let errored; + + process.env.SKIP_VALIDATION = "YeS"; + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + errored = error; + } + + expect(errored).toBeUndefined(); + + process.env.SKIP_VALIDATION = oldValue; + }); + + it('should allow to enable validation using "process.env.SKIP_VALIDATION"', () => { + const oldValue = process.env.SKIP_VALIDATION; + + process.env.SKIP_VALIDATION = "n"; + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + if (error.name !== "ValidationError") { + throw error; + } + + expect(error.message).toMatchSnapshot(); + } + + process.env.SKIP_VALIDATION = oldValue; + }); + + it('should allow to enable validation using "process.env.SKIP_VALIDATION" #2', () => { + const oldValue = process.env.SKIP_VALIDATION; + + process.env.SKIP_VALIDATION = " FaLse "; + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + if (error.name !== "ValidationError") { + throw error; + } + + expect(error.message).toMatchSnapshot(); + } + + process.env.SKIP_VALIDATION = oldValue; + }); + + it("should allow to disable validation using API", () => { + let errored; + + disableValidation(); + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + errored = error; + } + + expect(errored).toBeUndefined(); + + enableValidation(); + }); + + it("should allow to enable validation using API", () => { + disableValidation(); + enableValidation(); + + try { + validate(schemaTitle, { foo: "bar" }, { name: "NAME" }); + } catch (error) { + if (error.name !== "ValidationError") { + throw error; + } + + expect(error.message).toMatchSnapshot(); + } + }); + + it("should allow to enable and disable validation using API", () => { + enableValidation(); + expect(needValidate()).toBe(true); + + disableValidation(); + expect(needValidate()).toBe(false); + enableValidation(); + + enableValidation(); + enableValidation(); + expect(needValidate()).toBe(true); + + enableValidation(); + disableValidation(); + expect(needValidate()).toBe(false); + enableValidation(); + + enableValidation(); + expect(process.env.SKIP_VALIDATION).toBe("n"); + + disableValidation(); + expect(process.env.SKIP_VALIDATION).toBe("y"); + enableValidation(); + expect(process.env.SKIP_VALIDATION).toBe("n"); + }); });