@@ -65,184 +65,133 @@ Validator.prototype.compile = function(schema) {
6565 throw new Error ( "If the schema is an Array, must contain at least one element!" ) ;
6666 }
6767
68- const rules = flatten ( schema . map ( r => this . _processRule ( r , null , false ) ) ) ;
69- return this . _checkWrapper ( rules , true ) ;
68+ return this . compileSchemaType ( schema ) ;
69+ }
7070
71- } else if ( schema != null && typeof schema === "object" ) {
72- const rules = flatten ( Object . keys ( schema ) . map ( name => this . _processRule ( schema [ name ] , name , false ) ) ) ;
73- return this . _checkWrapper ( rules ) ;
71+ return this . compileSchemaObject ( schema ) ;
72+ } ;
7473
74+ Validator . prototype . compileSchemaObject = function ( schemaObject ) {
75+ if ( schemaObject == null || typeof schemaObject !== "object" || Array . isArray ( schemaObject ) ) {
76+ throw new Error ( "Invalid schema!" ) ;
77+ }
78+
79+ const self = this ;
80+ const checks = { } ;
81+ for ( let name in schemaObject ) {
82+ checks [ name ] = this . compileSchemaType ( schemaObject [ name ] ) ;
7583 }
7684
77- throw new Error ( "Invalid schema!" ) ;
85+ return function ( value , _ , path , parent ) {
86+ const errors = [ ] ;
87+ for ( let name in checks ) {
88+ const propertyPath = ( path ? path + "." : "" ) + name ;
89+ const res = checks [ name ] ( value [ name ] , schemaObject [ name ] , propertyPath , value ) ;
90+
91+ if ( res !== true ) {
92+ self . handleResult ( errors , propertyPath , res ) ;
93+ }
94+ }
95+
96+ return errors . length === 0 ? true : errors ;
97+ } ;
7898} ;
7999
80- /**
81- * Process a rule item & return checker functions
82- *
83- * @param { Object } rule
84- * @param { String } name
85- * @param { Boolean } iterate
86- */
87- Validator . prototype . _processRule = function ( rule , name , iterate ) {
88- const checks = [ ] ;
89-
90- if ( Array . isArray ( rule ) ) {
91- // Compile the multiple schemas
92- checks . push ( {
93- fn : this . compile ( rule ) ,
94- type : "_multi" ,
95- name : name ,
96- schema : rule ,
97- iterate : iterate
98- } ) ;
99-
100- return checks ;
100+ Validator . prototype . compileSchemaType = function ( schemaType ) {
101+ if ( Array . isArray ( schemaType ) ) {
102+ // Multiple rules
103+ const self = this ;
104+ const checks = schemaType . map ( r => this . compileSchemaType ( r ) ) ;
105+
106+ return function ( value , _ , path , parent ) {
107+ const errors = [ ] ;
108+ let validated = false ;
109+ for ( let i = 0 ; i < checks . length ; i ++ ) {
110+ const res = checks [ i ] ( value , schemaType [ i ] , path , parent ) ;
111+
112+ if ( res !== true ) {
113+ self . handleResult ( errors , path , res ) ;
114+ } else {
115+ validated = true ;
116+ }
117+ }
118+
119+ return validated ? true : errors ;
120+ } ;
101121 }
102122
103- if ( typeof rule === "string" ) {
104- rule = {
105- type : rule
123+ return this . compileSchemaRule ( schemaType ) ;
124+ } ;
125+
126+ Validator . prototype . compileSchemaRule = function ( schemaRule ) {
127+ if ( typeof schemaRule === "string" ) {
128+ schemaRule = {
129+ type : schemaRule
106130 } ;
107131 }
108132
109- if ( ! this . rules [ rule . type ] ) {
110- throw new Error ( "Invalid '" + rule . type + "' type in validator schema!" ) ;
133+ if ( ! this . rules [ schemaRule . type ] ) {
134+ throw new Error ( "Invalid '" + schemaRule . type + "' type in validator schema!" ) ;
111135 }
112136
137+ const self = this ;
138+ let checkContents = null ;
139+
140+ if ( schemaRule . type === "object" ) {
141+ if ( schemaRule . props ) {
142+ checkContents = this . compileSchemaObject ( schemaRule . props ) ;
143+ }
144+ } else if ( schemaRule . type === "array" ) {
145+ if ( schemaRule . items ) {
146+ checkContents = this . compileSchemaArray ( schemaRule . items ) ;
147+ }
148+ }
113149
114- /**
115- * !IMPORTANT!: For the functioning of multiRule cases it is important that
116- * pushing of object and array special rules is done in directly after this
117- * simple rule.
118- * If you which to push other checks, do it before the simple ones or after
119- * the array special case.
120- * See the comments in _checkWrapper for further explanation.
121- */
122- checks . push ( {
123- fn : this . rules [ rule . type ] ,
124- type : rule . type ,
125- name : name ,
126- schema : rule ,
127- iterate : iterate
128- } ) ;
150+ return function ( value , _ , path , parent ) {
151+ const res = self . checkRule ( value , schemaRule , path , parent ) ;
152+ if ( res !== true ) {
153+ return res ;
154+ }
129155
130- // Nested schema
131- if ( rule . type === "object" && rule . props ) {
132- // Compile the child schema
133- checks . push ( {
134- fn : this . compile ( rule . props ) ,
135- type : rule . type ,
136- name : name ,
137- schema : rule ,
138- iterate : iterate ,
139- secondPart : true //first part is the "primitive" typeof check above
140- } ) ;
141- }
156+ if ( checkContents ) {
157+ return checkContents ( value , schemaRule , path , parent ) ;
158+ }
142159
143- // Array schema
144- if ( rule . type === "array" && rule . items ) {
145- // Compile the array schema
146- checks . push ( {
147- fn : this . _checkWrapper ( this . _processRule ( rule . items , null , false ) ) ,
148- type : rule . type ,
149- name : name ,
150- schema : rule ,
151- iterate : true ,
152- secondPart : true //first part is the "primitive" typeof check above
153- } ) ;
160+ return true ;
154161 }
155-
156- return checks ;
157162} ;
158163
159- /**
160- * Create a wrapper function for compiled schema.
161- *
162- * @param {Array } rules
163- * @param {Boolean } isMultipleRules
164- */
165- Validator . prototype . _checkWrapper = function ( rules , isMultipleRules ) {
164+ Validator . prototype . compileSchemaArray = function ( schemaType ) {
166165 const self = this ;
166+ const checkArrayItem = this . compileSchemaType ( schemaType ) ;
167167
168- // Compiled validator function
169- return function ( obj , _schema , pathStack ) {
170- let errors = [ ] ;
171- const count = rules . length ;
172- for ( let i = 0 ; i < count ; i ++ ) {
173- const check = rules [ i ] ;
174- const schema = check . schema ;
175-
176- let value ;
177- let stack ;
178- if ( check . name ) {
179- value = obj [ check . name ] ;
180- stack = ( pathStack ? pathStack + "." : "" ) + check . name ;
181- } else {
182- value = obj ;
183- stack = pathStack ? pathStack : "" ;
184- }
168+ return function ( value , _ , path , parent ) {
169+ const errors = [ ] ;
170+ for ( let i = 0 ; i < value . length ; i ++ ) {
171+ const arrayItemPath = ( path ? path : "" ) + "[" + i + "]" ;
172+ const res = checkArrayItem ( value [ i ] , schemaType , arrayItemPath , value ) ;
185173
186- // Check required fields
187- if ( ( value === undefined || value === null ) ) {
188- if ( check . type === "forbidden" )
189- continue ;
174+ if ( res !== true ) {
175+ self . handleResult ( errors , arrayItemPath , res ) ;
176+ }
177+ }
190178
191- if ( schema . optional === true )
192- continue ;
179+ return errors . length === 0 ? true : errors ;
180+ }
181+ } ;
193182
194- if ( ! Array . isArray ( schema ) ) {
195- self . handleResult ( errors , stack , self . makeError ( "required" ) ) ;
196- continue ;
197- }
183+ Validator . prototype . checkRule = function ( value , schemaRule , path , parent ) {
184+ if ( value === undefined || value === null ) {
185+ if ( schemaRule . type === "forbidden" )
186+ return true ;
198187
199- } // else {
200- // Call the checker function
201- if ( check . iterate ) {
202- let errorInCurrentArray = false ;
203- const l = value . length ;
204- for ( let i = 0 ; i < l ; i ++ ) {
205- let _stack = stack + "[" + i + "]" ;
206- let res = check . fn . call ( self , value [ i ] , schema , _stack , obj ) ;
207- if ( res !== true ) {
208- errorInCurrentArray = true ;
209- self . handleResult ( errors , _stack , res ) ;
210- }
211- }
212- /**
213- * If this is second part of a multiRule array check and the array
214- * is valid, then the rule is valid.
215- */
216- if ( ! errorInCurrentArray && isMultipleRules && check . secondPart ) {
217- return true ;
218- }
219- } else {
220- let res = check . fn . call ( self , value , schema , stack , obj ) ;
221-
222- if ( isMultipleRules ) {
223- if ( res === true ) {
224- /**
225- * Object and array checks are divided into two internal checks. In case of a multiRule
226- * we have to make sure to check both parts. Thus we we continue to also do the second
227- * check if their is one.
228- */
229- const nextRule = rules [ i + 1 ] ;
230- if ( nextRule && nextRule . secondPart ) {
231- continue ;
232- }
233- // Jump out after first success and clear previous errors
234- return true ;
235- }
236- }
188+ if ( schemaRule . optional === true )
189+ return true ;
237190
238- if ( res !== true )
239- self . handleResult ( errors , stack , res ) ;
240- }
241- //}
242- }
191+ return this . makeError ( "required" ) ;
192+ }
243193
244- return errors . length === 0 ? true : errors ;
245- } ;
194+ return this . rules [ schemaRule . type ] . call ( this , value , schemaRule , path , parent ) ;
246195} ;
247196
248197/**
0 commit comments