mirror of https://github.com/lukechilds/ow.git
committed by
Sindre Sorhus
13 changed files with 147 additions and 77 deletions
@ -1,2 +1,5 @@ |
|||||
node_modules |
node_modules |
||||
yarn.lock |
yarn.lock |
||||
|
.nyc_output |
||||
|
coverage |
||||
|
dist |
||||
|
@ -1,66 +0,0 @@ |
|||||
'use strict'; |
|
||||
const is = require('@sindresorhus/is'); |
|
||||
|
|
||||
class ArgumentError extends Error { |
|
||||
constructor(message, context) { |
|
||||
super(message); |
|
||||
// TODO: Node does not preserve the error name in output when using the below, why?
|
|
||||
//Error.captureStackTrace(this, context);
|
|
||||
this.name = 'ArgumentError'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const ow = (value, predicate) => { |
|
||||
for (const validator of predicate.context.validators) { |
|
||||
const result = validator(value); |
|
||||
if (result) { |
|
||||
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
|
|
||||
throw new ArgumentError(result, ow); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const newInstance = (fn, context) => { |
|
||||
const instance = fn(context); |
|
||||
instance.context = context; |
|
||||
return instance; |
|
||||
}; |
|
||||
|
|
||||
const stringPredicates = context => ({ |
|
||||
minLength: number => { |
|
||||
context.validators.push(value => { |
|
||||
if (value.length < number) { |
|
||||
return `Expected string length to be minimum ${number}`; |
|
||||
} |
|
||||
}); |
|
||||
return newInstance(stringPredicates, context); |
|
||||
}, |
|
||||
get alphanumeric() { |
|
||||
context.validators.push(value => { |
|
||||
if (/[a-z\d]/gi.test(value)) { |
|
||||
return `Expected string to contain only alphanumeric characters but received \`${value}\``; |
|
||||
} |
|
||||
}); |
|
||||
return newInstance(stringPredicates, context); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
Object.defineProperty(ow, 'string', { |
|
||||
enumerable: true, |
|
||||
get() { |
|
||||
const instance = newInstance(stringPredicates, { |
|
||||
validators: [] |
|
||||
}); |
|
||||
|
|
||||
instance.context.validators.push(value => { |
|
||||
// TODO: Create a generic function for all types that can be used here
|
|
||||
if (!is.string(value)) { |
|
||||
return `Expected argument to be of type \`string\` but received type \`${is(value)}\``; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
return instance; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
module.exports = ow; |
|
@ -0,0 +1,3 @@ |
|||||
|
import { ow } from './ow'; |
||||
|
|
||||
|
export = ow; |
@ -0,0 +1,8 @@ |
|||||
|
export class ArgumentError extends Error { |
||||
|
constructor(message, context) { |
||||
|
super(message); |
||||
|
// TODO: Node does not preserve the error name in output when using the below, why?
|
||||
|
//Error.captureStackTrace(this, context);
|
||||
|
this.name = 'ArgumentError'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
import * as is from '@sindresorhus/is'; |
||||
|
|
||||
|
export type Validator = (value: any) => string | undefined; |
||||
|
|
||||
|
export interface Context { |
||||
|
validators: Validator[]; |
||||
|
} |
||||
|
|
||||
|
export class Predicate { |
||||
|
constructor( |
||||
|
type: string, |
||||
|
public context: Context = { validators: [] } |
||||
|
) { |
||||
|
this.context.validators.push(value => { |
||||
|
if (!is[type](value)) { |
||||
|
return `Expected argument to be of type \`${type}\` but received type \`${is(value)}\``; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
import { Predicate, Context } from './predicate'; |
||||
|
|
||||
|
export class StringPredicate extends Predicate { |
||||
|
|
||||
|
constructor(context?: Context) { |
||||
|
super('string', context); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Test a string to have a minimum length. |
||||
|
* |
||||
|
* @param number The minimum length of the string. |
||||
|
*/ |
||||
|
minLength(number: number) { |
||||
|
this.context.validators.push(value => { |
||||
|
if (value.length < number) { |
||||
|
return `Expected string length to be minimum ${number}`; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Test a string to be alphanumeric. |
||||
|
*/ |
||||
|
get alphanumeric() { |
||||
|
this.context.validators.push(value => { |
||||
|
if (!/^[a-z\d]+$/i.test(value)) { |
||||
|
return `Expected string to contain only alphanumeric characters but received \`${value}\``; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
import * as is from '@sindresorhus/is'; |
||||
|
import { ArgumentError } from './lib/argument-error'; |
||||
|
import { Predicate } from './lib/predicates/predicate'; |
||||
|
import { StringPredicate } from './lib/predicates/string'; |
||||
|
|
||||
|
export interface Ow { |
||||
|
(value: any, predicate: Predicate): void; |
||||
|
/** |
||||
|
* Test the value to be a string. |
||||
|
*/ |
||||
|
string?: StringPredicate; |
||||
|
} |
||||
|
|
||||
|
export const ow: Ow = (value: any, predicate: Predicate) => { |
||||
|
for (const validator of predicate.context.validators) { |
||||
|
const result = validator(value); |
||||
|
if (result) { |
||||
|
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
|
||||
|
throw new ArgumentError(result, ow); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
ow.string = new StringPredicate(); |
@ -0,0 +1,18 @@ |
|||||
|
import test from 'ava'; |
||||
|
import * as m from '..'; |
||||
|
|
||||
|
test('string', t => { |
||||
|
t.notThrows(() => m('foo', m.string)); |
||||
|
t.throws(() => m(12, m.string), 'Expected argument to be of type `string` but received type `number`'); |
||||
|
}); |
||||
|
|
||||
|
test('string.minLength', t => { |
||||
|
t.notThrows(() => m('foo', m.string.minLength(2))); |
||||
|
t.notThrows(() => m('foo', m.string.minLength(3))); |
||||
|
t.throws(() => m('foo', m.string.minLength(4)), 'Expected string length to be minimum 4'); |
||||
|
}); |
||||
|
|
||||
|
test('string.alphanumeric', t => { |
||||
|
t.notThrows(() => m('Foo123', m.string.alphanumeric)); |
||||
|
t.throws(() => m('Foo123!', m.string.alphanumeric), 'Expected string to contain only alphanumeric characters but received `Foo123!`'); |
||||
|
}); |
@ -1,6 +0,0 @@ |
|||||
import test from 'ava'; |
|
||||
import m from '.'; |
|
||||
|
|
||||
test('', t => { |
|
||||
|
|
||||
}); |
|
@ -0,0 +1,20 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "es2016", |
||||
|
"module": "commonjs", |
||||
|
"declaration": false, |
||||
|
"removeComments": false, |
||||
|
"pretty": true, |
||||
|
"allowUnreachableCode": false, |
||||
|
"allowUnusedLabels": false, |
||||
|
"noImplicitAny": false, |
||||
|
"noImplicitUseStrict": false, |
||||
|
"noFallthroughCasesInSwitch": true, |
||||
|
"allowSyntheticDefaultImports": true, |
||||
|
"outDir": "dist" |
||||
|
}, |
||||
|
"exclude": [ |
||||
|
"node_modules", |
||||
|
"dist" |
||||
|
] |
||||
|
} |
Loading…
Reference in new issue