Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 42 additions & 55 deletions lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
};
78 changes: 38 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
},
Expand Down
111 changes: 36 additions & 75 deletions test/snapshots/validate.test.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {},
Expand All @@ -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 {},
Expand All @@ -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"`.',
}
Binary file modified test/snapshots/validate.test.js.snap
Binary file not shown.