@@ -59,65 +59,75 @@ Validator.prototype.validate = function(obj, schema) {
5959 * @throws {Error } Invalid schema
6060 */
6161Validator . prototype . compile = function ( schema ) {
62+ const self = this ;
6263 if ( Array . isArray ( schema ) ) {
6364 // Multiple schemas
6465 if ( schema . length == 0 ) {
6566 throw new Error ( "If the schema is an Array, must contain at least one element!" ) ;
6667 }
6768
68- return this . compileSchemaType ( schema ) ;
69+ var rules = this . compileSchemaType ( schema ) ;
70+ return function ( value ) {
71+ return self . checkSchemaType ( value , rules , undefined , null ) ;
72+ }
6973 }
7074
71- return this . compileSchemaObject ( schema ) ;
75+ var rule = this . compileSchemaObject ( schema ) ;
76+ return function ( value ) {
77+ return self . checkSchemaObject ( value , rule , undefined , null ) ;
78+ }
7279} ;
7380
7481Validator . prototype . compileSchemaObject = function ( schemaObject ) {
7582 if ( schemaObject === null || typeof schemaObject !== "object" || Array . isArray ( schemaObject ) ) {
7683 throw new Error ( "Invalid schema!" ) ;
7784 }
78-
79- const self = this ;
80- const checks = Object . keys ( schemaObject ) . map ( name => ( { name : name , fn : this . compileSchemaType ( schemaObject [ name ] ) } ) ) ;
8185
82- return function ( value , _ , path , parent ) {
83- const errors = [ ] ;
84- const checksLength = checks . length ;
85- for ( let i = 0 ; i < checksLength ; i ++ ) {
86- const check = checks [ i ] ;
87- const propertyPath = ( path !== undefined ? path + "." : "" ) + check . name ;
88- const res = check . fn ( value [ check . name ] , undefined , propertyPath , value ) ;
86+ var compiledObject = Object . keys ( schemaObject ) . map ( name => {
87+ const compiledType = this . compileSchemaType ( schemaObject [ name ] ) ;
88+ return { name : name , compiledType : compiledType , isArray : Array . isArray ( compiledType ) } ;
89+ } ) ;
8990
90- if ( res !== true ) {
91- self . handleResult ( errors , propertyPath , res ) ;
92- }
91+ // Uncomment this line to use uncompiled object validator:
92+ // return compiledObject;
93+
94+ const sourceCode = [ ] ;
95+ sourceCode . push ( `let res;` ) ;
96+ sourceCode . push ( `let propertyPath;` ) ;
97+ sourceCode . push ( `const errors = [];` ) ;
98+ for ( let i = 0 ; i < compiledObject . length ; i ++ ) {
99+ const property = compiledObject [ i ] ;
100+ const name = property . name ;
101+ sourceCode . push ( `propertyPath = (path !== undefined ? path + ".${ name } " : "${ name } ");` ) ;
102+ if ( Array . isArray ( property . compiledType ) ) {
103+ sourceCode . push ( `res = this.checkSchemaType(value.${ name } , compiledObject[${ i } ].compiledType, propertyPath, value);` ) ;
104+ } else {
105+ sourceCode . push ( `res = this.checkSchemaRule(value.${ name } , compiledObject[${ i } ].compiledType, propertyPath, value);` ) ;
93106 }
107+ sourceCode . push ( `if (res !== true) {` ) ;
108+ sourceCode . push ( `\tthis.handleResult(errors, propertyPath, res);` ) ;
109+ sourceCode . push ( `}` ) ;
110+ }
94111
95- return errors . length === 0 ? true : errors ;
112+ sourceCode . push ( `return errors.length === 0 ? true : errors;` ) ;
113+
114+ var compiledObjectFunction = new Function ( "value" , "compiledObject" , "path" , "parent" , sourceCode . join ( "\n" ) ) ;
115+
116+ var self = this ;
117+ return function ( value , _unused , path , parent ) {
118+ return compiledObjectFunction . call ( self , value , compiledObject , path , parent ) ;
96119 } ;
97120} ;
98121
99122Validator . prototype . compileSchemaType = function ( schemaType ) {
100123 if ( Array . isArray ( schemaType ) ) {
101- // Multiple rules
102- const self = this ;
103- const checks = schemaType . map ( r => this . compileSchemaType ( r ) ) ;
104-
105- return function ( value , _ , path , parent ) {
106- const errors = [ ] ;
107- const checksLength = checks . length ;
108- for ( let i = 0 ; i < checksLength ; i ++ ) {
109- const res = checks [ i ] ( value , undefined , path , parent ) ;
110-
111- if ( res !== true ) {
112- self . handleResult ( errors , path , res ) ;
113- } else {
114- // Jump out after first success and clear previous errors
115- return true ;
116- }
117- }
124+ // Multiple rules, flatten to array of compiled SchemaRule
125+ const rules = flatten ( schemaType . map ( r => this . compileSchemaType ( r ) ) ) ;
126+ if ( rules . length == 1 ) {
127+ return rules [ 0 ] ;
128+ }
118129
119- return errors ;
120- } ;
130+ return rules ;
121131 }
122132
123133 return this . compileSchemaRule ( schemaType ) ;
@@ -130,69 +140,116 @@ Validator.prototype.compileSchemaRule = function(schemaRule) {
130140 } ;
131141 }
132142
133- const checkRule = this . rules [ schemaRule . type ] ;
134- if ( ! checkRule ) {
143+ const ruleFunction = this . rules [ schemaRule . type ] ;
144+ if ( ! ruleFunction ) {
135145 throw new Error ( "Invalid '" + schemaRule . type + "' type in validator schema!" ) ;
136146 }
137147
138- const self = this ;
139- let checkContents = null ;
148+ let dataParameter = null ;
149+ let dataFunction = null ;
150+
151+ if ( schemaRule . type === "object" && schemaRule . props ) {
152+ dataParameter = this . compileSchemaObject ( schemaRule . props ) ;
153+ dataFunction = this . checkSchemaObject ;
154+ } else if ( schemaRule . type === "array" && schemaRule . items ) {
155+ dataParameter = this . compileSchemaType ( schemaRule . items ) ;
156+ dataFunction = this . checkSchemaArray ;
157+ }
140158
141- if ( schemaRule . type === "object" ) {
142- if ( schemaRule . props ) {
143- checkContents = this . compileSchemaObject ( schemaRule . props ) ;
144- }
145- } else if ( schemaRule . type === "array" ) {
146- if ( schemaRule . items ) {
147- checkContents = this . compileSchemaArray ( schemaRule . items ) ;
159+ return {
160+ schemaRule : schemaRule ,
161+ ruleFunction : ruleFunction ,
162+ dataFunction : dataFunction ,
163+ dataParameter : dataParameter
164+ } ;
165+ } ;
166+
167+ Validator . prototype . checkSchemaObject = function ( value , compiledObject , path , parent ) {
168+
169+ if ( compiledObject instanceof Function ) {
170+ return compiledObject ( value , undefined , path , parent ) ;
171+ }
172+
173+ const errors = [ ] ;
174+ const checksLength = compiledObject . length ;
175+ for ( let i = 0 ; i < checksLength ; i ++ ) {
176+ const check = compiledObject [ i ] ;
177+ const propertyPath = ( path !== undefined ? path + "." : "" ) + check . name ;
178+ const res = this . checkSchemaType ( value [ check . name ] , check . compiledType , propertyPath , value ) ;
179+
180+ if ( res !== true ) {
181+ this . handleResult ( errors , propertyPath , res ) ;
148182 }
149183 }
150184
151- return function ( value , _ , path , parent ) {
185+ return errors . length === 0 ? true : errors ;
186+ }
187+
188+ Validator . prototype . checkSchemaType = function ( value , compiledType , path , parent ) {
189+ if ( Array . isArray ( compiledType ) ) {
152190 const errors = [ ] ;
153- if ( value === undefined || value === null ) {
154- if ( schemaRule . type === "forbidden" )
155- return true ;
191+ const checksLength = compiledType . length ;
192+ for ( let i = 0 ; i < checksLength ; i ++ ) {
193+ // Always compiled to list of rules
194+ const res = this . checkSchemaRule ( value , compiledType [ i ] , path , parent ) ;
156195
157- if ( schemaRule . optional === true )
196+ if ( res !== true ) {
197+ this . handleResult ( errors , path , res ) ;
198+ } else {
199+ // Jump out after first success and clear previous errors
158200 return true ;
159-
160- self . handleResult ( errors , path , self . makeError ( "required" ) ) ;
161- return errors ;
201+ }
162202 }
163203
164- const res = checkRule . call ( self , value , schemaRule , path , parent ) ;
204+ return errors ;
205+ }
206+
207+ return this . checkSchemaRule ( value , compiledType , path , parent ) ;
208+ }
209+
210+ Validator . prototype . checkSchemaArray = function ( value , compiledArray , path , parent ) {
211+ const errors = [ ] ;
212+ const valueLength = value . length ;
213+
214+ for ( let i = 0 ; i < valueLength ; i ++ ) {
215+ const itemPath = ( path !== undefined ? path : "" ) + "[" + i + "]" ;
216+ const res = this . checkSchemaType ( value [ i ] , compiledArray , itemPath , value ) ;
217+
165218 if ( res !== true ) {
166- self . handleResult ( errors , path , res ) ;
167- return errors ;
219+ this . handleResult ( errors , itemPath , res ) ;
168220 }
221+ }
169222
170- if ( checkContents !== null ) {
171- return checkContents ( value , undefined , path , parent ) ;
172- }
223+ return errors . length === 0 ? true : errors ;
224+ }
173225
174- return true ;
175- }
176- } ;
226+ Validator . prototype . checkSchemaRule = function ( value , compiledRule , path , parent ) {
227+ const schemaRule = compiledRule . schemaRule ;
177228
178- Validator . prototype . compileSchemaArray = function ( schemaType ) {
179- const self = this ;
180- const checkArrayItem = this . compileSchemaType ( schemaType ) ;
229+ if ( value === undefined || value === null ) {
230+ if ( schemaRule . type === "forbidden" )
231+ return true ;
232+
233+ if ( schemaRule . optional === true )
234+ return true ;
181235
182- return function ( value , _ , path , parent ) {
183236 const errors = [ ] ;
184- const valueLength = value . length ;
185- for ( let i = 0 ; i < valueLength ; i ++ ) {
186- const arrayItemPath = ( path !== undefined ? path : "" ) + "[" + i + "]" ;
187- const res = checkArrayItem ( value [ i ] , undefined , arrayItemPath , value ) ;
237+ this . handleResult ( errors , path , this . makeError ( "required" ) ) ;
238+ return errors ;
239+ }
188240
189- if ( res !== true ) {
190- self . handleResult ( errors , arrayItemPath , res ) ;
191- }
192- }
241+ const res = compiledRule . ruleFunction . call ( this , value , schemaRule , path , parent ) ;
242+ if ( res !== true ) {
243+ const errors = [ ] ;
244+ this . handleResult ( errors , path , res ) ;
245+ return errors ;
246+ }
193247
194- return errors . length === 0 ? true : errors ;
248+ if ( compiledRule . dataFunction !== null ) {
249+ return compiledRule . dataFunction . call ( this , value , compiledRule . dataParameter , path , parent ) ;
195250 }
251+
252+ return true ;
196253} ;
197254
198255/**
0 commit comments