-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from reyx7/development
Development
- Loading branch information
Showing
27 changed files
with
2,314 additions
and
453 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,35 @@ | ||
import resolve from "rollup-plugin-node-resolve"; | ||
import babel from "rollup-plugin-babel"; | ||
|
||
export default { | ||
input: "src/index.js", | ||
output: { | ||
file: "lib/index.js", | ||
format: "cjs" | ||
export default [ | ||
// CommonJS | ||
{ | ||
input: "src/index.js", | ||
output: { | ||
file: "lib/cjs.js", | ||
format: "cjs", | ||
indent: false | ||
}, | ||
plugins: [ | ||
resolve(), | ||
babel({ | ||
exclude: "node_modules/**" // only transpile our source code | ||
}) | ||
] | ||
}, | ||
plugins: [ | ||
resolve(), | ||
babel({ | ||
exclude: "node_modules/**" // only transpile our source code | ||
}) | ||
] | ||
}; | ||
// ES | ||
{ | ||
input: "src/index.js", | ||
output: { | ||
file: "lib/es.js", | ||
format: "es", | ||
indent: false | ||
}, | ||
plugins: [ | ||
resolve(), | ||
babel({ | ||
exclude: "node_modules/**" // only transpile our source code | ||
}) | ||
] | ||
} | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,76 @@ | ||
import { getErrorMessage, fieldWrapper } from "./utils"; | ||
import rules from "./rules/index"; | ||
import allRules from "./rules/index"; | ||
import { rulesWrapper, resolveArgs, isOptionalSuccess } from "./utils"; | ||
|
||
class Field { | ||
#rules = []; | ||
const isFail = (isRuleSuccess, constraint, strict, values) => | ||
strict | ||
? constraint && constraint.validate(values) !== isRuleSuccess | ||
: constraint && constraint.validate(values) && !isRuleSuccess; | ||
|
||
_add(rule) { | ||
// push this rule given in param to rules array | ||
this.#rules.push(rule); | ||
// and return context to continue the chain of rules | ||
const messageTemplate = (message, value, field) => | ||
message | ||
.replace("%{value}", value) | ||
.replace("%{field}", field) | ||
.trim(); | ||
|
||
export class Field { | ||
#rules = null; | ||
|
||
#not = null; | ||
|
||
constructor(rules = [], not = false) { | ||
this.#rules = rules; | ||
this.#not = not; | ||
} | ||
|
||
_clone() { | ||
return [this.#rules, this.#not]; | ||
} | ||
|
||
get any() { | ||
return this; | ||
} | ||
|
||
validate(value, { message = false } = {}) { | ||
get not() { | ||
return new Field([...this.#rules], true); | ||
} | ||
|
||
validate( | ||
value, | ||
{ verbose = false, values = {}, strict = true, field = "" } = {} | ||
) { | ||
for (let i = 0; i < this.#rules.length; i += 1) { | ||
// Get validator function and options object from | ||
// the current rule. | ||
const { validator, options } = this.#rules[i]; | ||
if (validator(value) !== true) { | ||
// the rule is fail , we check if the user want | ||
// a error message indicator or boolean. | ||
if (message === true) return getErrorMessage(options); | ||
return false; | ||
const { | ||
name, | ||
rule, | ||
args, | ||
not, | ||
options: { constraint, message } | ||
} = this.#rules[i]; | ||
|
||
const isRuleSuccess = not !== rule(...resolveArgs(args, values), value); | ||
|
||
const isOptionalRule = | ||
name === "required" && | ||
(not || (strict && constraint && !constraint.validate(values))); | ||
|
||
if (isOptionalRule) { | ||
if (isOptionalSuccess(value, this.#rules[i + 1].name)) { | ||
break; | ||
} | ||
} else if ( | ||
(!isRuleSuccess && !constraint) || | ||
(name === "required" && | ||
(!not && strict && constraint) && | ||
!isRuleSuccess) || | ||
isFail(isRuleSuccess, constraint, strict, values) | ||
) { | ||
return verbose === true | ||
? messageTemplate(message, value, field) | ||
: false; | ||
} | ||
} | ||
// All rules are valide we return true to indicate that | ||
return true; | ||
} | ||
} | ||
|
||
export default fieldWrapper(rules)(Field); | ||
export default rulesWrapper(allRules)(Field); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,135 @@ | ||
type Options = { | ||
message: string; | ||
}; | ||
interface Options { | ||
message?: string; | ||
constraint?: (target: "#" | ["#", "#"], condition: Field) => any; | ||
} | ||
|
||
type Rules = { | ||
required(options: Options): Rules; | ||
equals(value: any, options: Options): Rules; | ||
}; | ||
type RelatedField = ["#", Function]; | ||
|
||
type Rules<T, R = T> = { | ||
required(options?: Options): Rules<T, R>; | ||
not: Rules<T, R>; | ||
equals(value: any | RelatedField, options?: Options): Rules<T, R>; | ||
exact(value: any | RelatedField, options?: Options): Rules<T, R>; | ||
none(options?: Options): Rules<T, R>; | ||
empty(options?: Options): Rules<T, R>; | ||
oneOf( | ||
value: Array<any> | Object | RelatedField, | ||
options?: Options | ||
): Rules<T, R>; | ||
exactOneOf( | ||
value: Array<any> | Object | RelatedField, | ||
options?: Options | ||
): Rules<T, R>; | ||
} & T & | ||
R; | ||
|
||
type IteratorRules<T = StringRules | ArrayRules> = { | ||
min(value: Number, options: Options): IteratorRules<T> & Rules & T; | ||
max(value: Number, options: Options): IteratorRules<T> & Rules & T; | ||
lengthEquals(value: Number, options: Options): IteratorRules<T> & Rules & T; | ||
lengthEquals(value: Number | RelatedField, options?: Options): IT<T>; | ||
lengthLess(value: Number | RelatedField, options?: Options): IT<T>; | ||
lengthLessOrEquals(value: Number | RelatedField, options?: Options): IT<T>; | ||
max(value: Number | RelatedField, options?: Options): IT<T>; | ||
lengthGreater(value: Number | RelatedField, options?: Options): IT<T>; | ||
lengthGreaterOrEquals(value: Number | RelatedField, options?: Options): IT<T>; | ||
min(value: Number | RelatedField, options?: Options): IT<T>; | ||
lengthBetween( | ||
min: Number, | ||
max: Number, | ||
options: Options | ||
): IteratorRules<T> & Rules & T; | ||
min: Number | RelatedField, | ||
max: Number | RelatedField, | ||
options?: Options | ||
): IT<T>; | ||
lengthBetweenOrEquals( | ||
min: Number | RelatedField, | ||
max: Number | RelatedField, | ||
options?: Options | ||
): IT<T>; | ||
} & T; | ||
|
||
type StringRules = { | ||
first(value: String | RelatedField, options?: Options): STRING; | ||
last(value: String | RelatedField, options?: Options): STRING; | ||
last(value: String | RelatedField, options?: Options): STRING; | ||
pattern(value: String | RelatedField, options?: Options): STRING; | ||
ipv4(value: String | RelatedField, options?: Options): STRING; | ||
ipv6(value: String | RelatedField, options?: Options): STRING; | ||
ip(value: String | RelatedField, options?: Options): STRING; | ||
url(value: String | RelatedField, options?: Options): STRING; | ||
alpha(value: String | RelatedField, options?: Options): STRING; | ||
alphaNum(value: String | RelatedField, options?: Options): STRING; | ||
num(value: String | RelatedField, options?: Options): STRING; | ||
bool(value: String | RelatedField, options?: Options): STRING; | ||
domain(value: String | RelatedField, options?: Options): STRING; | ||
host(value: String | RelatedField, options?: Options): STRING; | ||
email(value: String | RelatedField, options?: Options): STRING; | ||
}; | ||
|
||
type NumberRules = { | ||
between(min: Number, max: Number, options: Options): NumberRules & Rules; | ||
less(value: Number | RelatedField, options?: Options): NUBMER; | ||
lessOrEquals(value: Number | RelatedField, options?: Options): NUBMER; | ||
greater(value: Number | RelatedField, options?: Options): NUBMER; | ||
greaterOrEquals(value: Number | RelatedField, options?: Options): NUBMER; | ||
positive(value: Number | RelatedField, options?: Options): NUBMER; | ||
negative(value: Number | RelatedField, options?: Options): NUBMER; | ||
between( | ||
min: Number | RelatedField, | ||
max: Number | RelatedField, | ||
options?: Options | ||
): NUBMER; | ||
betweenOrEquals( | ||
min: Number | RelatedField, | ||
max: Number | RelatedField, | ||
options?: Options | ||
): NUBMER; | ||
}; | ||
|
||
type StringRules = { | ||
startsWith( | ||
startValue: String, | ||
options: Options | ||
): StringRules & Rules & IteratorRules<StringRules>; | ||
endsWith( | ||
endValue: String, | ||
options: Options | ||
): StringRules & Rules & IteratorRules<StringRules>; | ||
type ArrayRules = { | ||
allEquals(value: any | RelatedField, options?: Options): ARRAY; | ||
allExact(value: any | RelatedField, options?: Options): ARRAY; | ||
}; | ||
|
||
type ArrayRules = {}; | ||
|
||
type NumberRules = { | ||
between(min: Number, max: Number, options: Options): NumberRules & Rules; | ||
type BooleanRules = { | ||
falsy(options?: Options): BOOLEAN; | ||
truthy(options?: Options): BOOLEAN; | ||
}; | ||
|
||
type BaseRules = { | ||
string(options: Options): StringRules & IteratorRules<StringRules> & Rules; | ||
number(options: Options): Rules; | ||
boolean(options: Options): Rules; | ||
object(options: Options): Rules; | ||
}; | ||
type TypeSchema = { | ||
validate(values: Object, options: Options): Object | Boolean; | ||
type ObjectRules = { | ||
hasKey(value: String, options?: Options): OBJECT; | ||
hasValue(value: any, options?: Options): OBJECT; | ||
}; | ||
|
||
export function field(): BaseRules; | ||
export function typeSchema(schema: Object): BaseRules; | ||
type STRING = StringRules & | ||
IT<StringRules> & | ||
Rules<StringRules, IT<StringRules>>; | ||
|
||
type NUBMER = NumberRules & Rules<NumberRules>; | ||
|
||
type BOOLEAN = BooleanRules & Rules<BooleanRules>; | ||
|
||
type OBJECT = ObjectRules & Rules<ObjectRules>; | ||
|
||
type ARRAY = ArrayRules & IT<ArrayRules> & Rules<ArrayRules, IT<ArrayRules>>; | ||
|
||
type IT<T> = IteratorRules<T> & Rules<T, IteratorRules<T>>; | ||
|
||
interface Field { | ||
string(options?: Options): STRING; | ||
number(options?: Options): NUBMER; | ||
boolean(options?: Options): BOOLEAN; | ||
array(options?: Options): ARRAY; | ||
object(options?: Options): OBJECT; | ||
any: STRING & NUBMER & BOOLEAN & ARRAY & OBJECT; | ||
} | ||
|
||
interface V<T = any> { | ||
validate( | ||
values: Object, | ||
options?: { verbose: false; strict: true; bool: false; async: false } | ||
): Object | Boolean; | ||
} | ||
|
||
export function Field(): Field; | ||
|
||
export function Schema<T = Object>( | ||
schema: T, | ||
options?: { verbose: false; strict: true; bool: false; async: false } | ||
): V & { [K in keyof T]: V<T> }; | ||
|
||
export function When(target: "#" | ["#", "#"], condition: Field): any; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import Field from "./field"; | ||
import Schema from "./schema"; | ||
import FieldObject from "./field"; | ||
import SchemaObject from "./schema"; | ||
import WhenObject from "./when"; | ||
|
||
export const field = () => new Field(); | ||
export const Field = () => new FieldObject(); | ||
|
||
export const typeSchema = Schema; | ||
export const When = (name, rule) => new WhenObject([{ name, rule }]); | ||
|
||
export const Schema = SchemaObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { equals, exact } from "./generic"; | ||
|
||
export const array = v => v instanceof Array; | ||
|
||
export const allEquals = (value, arr) => { | ||
for (let i = 0; i < arr.length; i += 1) { | ||
if (!equals(value, arr[i])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
|
||
export const allExact = (value, arr) => { | ||
for (let i = 0; i < arr.length; i += 1) { | ||
if (!exact(value, arr[i])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; |
Oops, something went wrong.