diff --git a/lib/validate.js b/lib/validate.js index 2fe1003..0ab2b0a 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -8,73 +8,60 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of this Source Code Form. */ -const Joi = require('@hapi/joi'); +const { + refinement, + any, + nullable, + type, + partial, + array, + union, + number, + boolean, + string, + func, + literal, + never, + validate +} = require('superstruct'); const isPromise = require('is-promise'); -// until https://github.com/hapijs/joi/issues/1690 is fixed, we have to have a base of any(). once -// they get that fixed, we can use object() here, which is what we should be using -const joi = Joi.extend((base) => { - return { - base, - language: { - promise: 'must be a promise-like object' - }, - name: 'any', - rules: [ - { - name: 'promise', - validate(params, value, state, options) { - if (!isPromise(value)) { - return this.createError('any.promise', { v: value }, state, options); - } +const promise = refinement(any(), 'promise-like', (value) => isPromise(value)); - return value; - } - } - ] - }; -}); - -// until https://github.com/hapijs/joi/issues/1691 is fixed, we have to use `any().allow()`, -// `any().forbidden()` and `any().promise()` -const { any, array, boolean, func, number, object, string, validate } = joi.bind(); +const port = refinement(number(), 'port', (value) => Number.isInteger(value) && value <= 65535); module.exports = { validate(options) { - const keys = { + const schema = partial({ allowMany: boolean(), - // get it together, Prettier! https://github.com/prettier/prettier/issues/3621 - // prettier-ignore - client: [object().keys({ address: string(), retry: boolean(), silent: boolean() }).allow(null)], - compress: [boolean().allow(null)], - headers: object().allow(null), - historyFallback: [boolean(), object()], + client: partial({ + address: string(), + retry: boolean(), + silent: boolean() + }), + compress: nullable(boolean()), + headers: nullable(type({})), + historyFallback: union([boolean(), type({})]), hmr: boolean(), - // prettier-ignore - host: [any().promise().allow(null), string()], - http2: [boolean(), object()], - https: object().allow(null), + host: nullable(union([promise, string()])), + http2: union([boolean(), type({})]), + https: nullable(type({})), liveReload: boolean(), - log: object().keys({ level: string(), timestamp: boolean() }), + log: partial({ level: string(), timestamp: boolean() }), middleware: func(), - open: [boolean(), object()], - // prettier-ignore - port: [number().integer().max(65535), any().promise()], - progress: [boolean(), string().valid('minimal')], - ramdisk: [boolean(), object()], - secure: any().forbidden(), - // prettier-ignore - static: [ - string().allow(null), - array().items(string()), - object().keys({ glob: array().items(string()), options: object() }) - ], + open: union([boolean(), type({})]), + port: union([port, promise]), + progress: union([boolean(), literal('minimal')]), + ramdisk: union([boolean(), type({})]), + secure: never(), + static: nullable( + union([string(), array(string()), partial({ glob: array(string()), options: type({}) })]) + ), status: boolean(), waitForBuild: boolean() - }; - const schema = object().keys(keys); - const results = validate(options, schema); + }); + const [error, value] = validate(options, schema); - return results; + return { error, value }; } }; diff --git a/package-lock.json b/package-lock.json index 94d0ea6..58b5252 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1048,46 +1048,10 @@ "arrify": "^1.0.1" } }, - "@hapi/address": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.0.0.tgz", - "integrity": "sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==" - }, - "@hapi/hoek": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-6.2.4.tgz", - "integrity": "sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A==" - }, - "@hapi/joi": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.0.tgz", - "integrity": "sha512-n6kaRQO8S+kepUTbXL9O/UOL788Odqs38/VOfoCrATDtTvyfiO3fgjlSRaNkHabpTLgM7qru9ifqXlXbXk8SeQ==", - "requires": { - "@hapi/address": "2.x.x", - "@hapi/hoek": "6.x.x", - "@hapi/marker": "1.x.x", - "@hapi/topo": "3.x.x" - } - }, - "@hapi/marker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@hapi/marker/-/marker-1.0.0.tgz", - "integrity": "sha512-JOfdekTXnJexfE8PyhZFyHvHjt81rBFSAbTIRAhF2vv/2Y1JzoKsGqxH/GpZJoF7aEfYok8JVcAHmSz1gkBieA==" - }, - "@hapi/topo": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.2.tgz", - "integrity": "sha512-r+aumOqJ5QbD6aLPJWqVjMAPsx5pZKz+F5yPqXZ/WWG9JTtHbQqlzrJoknJ0iJxLj9vlXtmpSdjlkszseeG8OA==", - "requires": { - "@hapi/hoek": "8.x.x" - }, - "dependencies": { - "@hapi/hoek": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.0.2.tgz", - "integrity": "sha512-O6o6mrV4P65vVccxymuruucb+GhP2zl9NLCG8OdoFRS8BEGw3vwpPp20wpAtpbQQxz1CEUtmxJGgWhjq1XA3qw==" - } - } + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" }, "@istanbuljs/load-nyc-config": { "version": "1.0.0", @@ -11806,6 +11770,11 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "superstruct": { + "version": "0.10.12", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.10.12.tgz", + "integrity": "sha512-FiNhfegyytDI0QxrrEoeGknFM28SnoHqCBpkWewUm8jRNj74NVxLpiiePvkOo41Ze/aKMSHa/twWjNF81mKaQQ==" + }, "supertap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supertap/-/supertap-1.0.0.tgz", @@ -12684,6 +12653,35 @@ "execa": "^2.0.0" }, "dependencies": { + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==" + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", diff --git a/package.json b/package.json index 40e55bd..25e8ac0 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "webpack": "^4.20.2" }, "dependencies": { - "@hapi/joi": "^15.1.0", "chalk": "^4.0.0", "connect-history-api-fallback": "^1.5.0", "globby": "^11.0.0", @@ -58,6 +57,7 @@ "read-pkg-up": "^7.0.1", "rimraf": "^3.0.2", "strip-ansi": "^6.0.0", + "superstruct": "^0.10.12", "webpack-plugin-ramdisk": "^0.1.2", "ws": "^7.1.0" }, diff --git a/test/snapshots/validate.test.js.md b/test/snapshots/validate.test.js.md index a5969d5..b71d228 100644 --- a/test/snapshots/validate.test.js.md +++ b/test/snapshots/validate.test.js.md @@ -8,76 +8,47 @@ Generated by [AVA](https://ava.li). > Snapshot 1 - ValidationError (Error) { - _object: { - foo: 'bar', - }, - annotate: Function {}, - details: [ + TypeError (StructError) { + branch: [ { - context: { - child: 'foo', - key: 'foo', - label: 'foo', - value: 'bar', - }, - message: '"foo" is not allowed', - path: [ - 'foo', - ], - type: 'object.allowUnknown', + foo: 'bar', }, + 'bar', + ], + failures: GeneratorFunction failures {}, + path: [ + 'foo', ], - isJoi: true, - message: '"foo" is not allowed', + type: 'never', + value: 'bar', + message: 'Expected a value of type `never` for `foo` but received `"bar"`.', } ## promise > Snapshot 1 - ValidationError (Error) { - _object: { - host: 0, - port: '0', - }, - annotate: Function {}, - details: [ - { - context: { - key: 'host', - label: 'host', - v: 0, - }, - message: '"host" must be a promise-like object', - path: [ - 'host', - ], - type: 'any.promise', - }, + TypeError (StructError) { + branch: [ { - context: { - key: 'host', - label: 'host', - value: 0, - }, - message: '"host" must be a string', - path: [ - 'host', - ], - type: 'string.base', + host: 0, + port: '0', }, + 0, + ], + failures: GeneratorFunction failures {}, + path: [ + 'host', ], - isJoi: true, - message: 'child "host" fails because ["host" must be a promise-like object, "host" must be a string]', + type: 'promise-like | string', + value: 0, + message: 'Expected a value of type `promise-like | string` for `host` but received `0`.', } > Snapshot 2 { - catch: Function catch {}, - error: null, - then: Function then {}, + error: undefined, value: { host: Promise {}, port: Promise {}, @@ -87,9 +58,7 @@ Generated by [AVA](https://ava.li). > Snapshot 3 { - catch: Function catch {}, - error: null, - then: Function then {}, + error: undefined, value: { host: { then: Function then {}, @@ -104,26 +73,18 @@ Generated by [AVA](https://ava.li). > Snapshot 1 - ValidationError (Error) { - _object: { - batman: 'nanananana', - }, - annotate: Function {}, - details: [ + TypeError (StructError) { + branch: [ { - context: { - child: 'batman', - key: 'batman', - label: 'batman', - value: 'nanananana', - }, - message: '"batman" is not allowed', - path: [ - 'batman', - ], - type: 'object.allowUnknown', + batman: 'nanananana', }, + 'nanananana', + ], + failures: GeneratorFunction failures {}, + path: [ + 'batman', ], - isJoi: true, - message: '"batman" is not allowed', + type: 'never', + value: 'nanananana', + message: 'Expected a value of type `never` for `batman` but received `"nanananana"`.', } diff --git a/test/snapshots/validate.test.js.snap b/test/snapshots/validate.test.js.snap index e89873e..e106439 100644 Binary files a/test/snapshots/validate.test.js.snap and b/test/snapshots/validate.test.js.snap differ