Skip to content

Commit

Permalink
precompiled dot templates, dot is devDependency, closes #3
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Jun 17, 2015
1 parent 5b62a32 commit 5f36f17
Show file tree
Hide file tree
Showing 56 changed files with 1,441 additions and 17 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# ajv - Another JSON Schema Validator

One of the fastest JSON Schema validators for node.js. It uses [doT templates](https://github.com/olado/doT) to generate super-fast validating functions.
One of the fastest JSON Schema validators for node.js.

It uses precompiled [doT templates](https://github.com/olado/doT) to generate super-fast validating functions.


[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv)
[![npm version](https://badge.fury.io/js/ajv.svg)](http://badge.fury.io/js/ajv)
Expand Down Expand Up @@ -150,6 +153,18 @@ git submodule update --init
npm test
```


## Contributing

All validation functions are generated using doT templates in dot folder. Templates are precompiled so doT is not a run-time dependency.

`bin/compile_dots` to compile templates to dotjs folder

`bin/watch_dots` to automatically compile templates when files in dot folder change

There is pre-commit hook that runs compile_dots and tests.


## Changes history

##### 0.5.0
Expand Down
3 changes: 3 additions & 0 deletions bin/compile_dots
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node ./bin/compile_dots.js
22 changes: 22 additions & 0 deletions bin/compile_dots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//compile doT templates to js functions

var glob = require('glob')
, fs = require('fs')
, path = require('path')
, doT = require('dot')
, beautify = require('js-beautify').js_beautify;

var defs = fs.readFileSync(path.join(__dirname, '../lib/dot/definitions.def'));
var files = glob.sync('../lib/dot/*.jst', { cwd: __dirname });

files.forEach(function (f) {
var template = fs.readFileSync(path.join(__dirname, f));
var code = doT.compile(template, { definitions: defs });
code = "'use strict';\nmodule.exports = " + code.toString();
code = code.replace(/out\s*\+=\s*'\s*';/g, '');
code = beautify(code, { indent_size: 2 }) + '\n';
var targetFile = f.replace('../lib/dot', '').replace('.jst', '.js')
, targetPath = path.join(__dirname, '../lib/dotjs', targetFile);
fs.writeFileSync(targetPath, code);
console.log('compiled', targetFile);
});
2 changes: 2 additions & 0 deletions bin/watch_dots
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
./node_modules/.bin/watch 'node bin/compile_dots.js' ./lib/dot
7 changes: 2 additions & 5 deletions lib/compile/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
'use strict';

var doT = require('dot')
, fs = require('fs')
, resolve = require('./resolve')
var resolve = require('./resolve')
, util = require('./util')
, equal = require('./equal');

try { var beautify = require('js-beautify').js_beautify; } catch(e) {}

var RULES = require('./rules')
, validateTemplate = fs.readFileSync(__dirname + '/validate.dot.js')
, validateGenerator = doT.compile(validateTemplate, { definitions: RULES.defs });
, validateGenerator = require('../dotjs/validate');

module.exports = compile;

Expand Down
10 changes: 2 additions & 8 deletions lib/compile/rules/index.js → lib/compile/rules.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
'use strict';

var fs = require('fs')
, doT = require('dot')
, util = require('../util');

var defs = fs.readFileSync(__dirname + '/definitions.def.js');
var util = require('./util');

var RULES = module.exports = [
{ type: 'number',
Expand All @@ -18,16 +14,14 @@ var RULES = module.exports = [
{ rules: [ '$ref', 'enum', 'not', 'anyOf', 'oneOf', 'allOf' ] }
];

RULES.defs = defs;
RULES.all = [ 'type', 'additionalProperties', 'patternProperties' ];

RULES.forEach(function (group) {
group.rules = group.rules.map(function (keyword) {
RULES.all.push(keyword);
var template = fs.readFileSync(__dirname + '/' + keyword + '.dot.js');
return {
keyword: keyword,
code: doT.compile(template, { definitions: defs })
code: require('../dotjs/' + keyword)
};
});
});
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
60 changes: 60 additions & 0 deletions lib/dotjs/$ref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict';
module.exports = function anonymous(it) {
var out = ' ';
var $lvl = it.level,
$dataLvl = it.dataLevel,
$schema = it.schema['$ref'],
$schemaPath = it.schemaPath + '.' + '$ref',
$breakOnError = !it.opts.allErrors;
var $data = 'data' + ($dataLvl || ''),
$valid = 'valid' + $lvl,
$errs = 'errs' + $lvl;
if (it.opts._debug) {
out += ' console.log(\'Keyword ' + ('$ref') + '\'); ';
}
if ($schema == '#' || $schema == '#/') {
if (it.isRoot) {
if ($breakOnError && it.wasTop) {
out += ' if (! ' + ('validate') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) return false; else { ';
} else {
out += ' var errors' + ($lvl) + ' = validate.errors; if (! ' + ('validate') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (errors' + ($lvl) + ' !== null) validate.errors = errors' + ($lvl) + '.concat(validate.errors); errors = validate.errors.length; } ';
if ($breakOnError) {
out += ' else { ';
}
}
} else {
var $v = 'v' + $lvl;
out += ' var ' + ($v) + ' = root.refVal[0]; if (! ' + ($v) + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (validate.errors === null) validate.errors = ' + ($v) + '.errors; else validate.errors = validate.errors.concat(' + ($v) + '.errors); errors = validate.errors.length; } ';
if ($breakOnError) {
out += ' else { ';
}
}
} else {
var $refVal = it.resolveRef(it.baseId, $schema, it.rootId);
if ($refVal === undefined) {
if (it.wasTop && $breakOnError) {
out += ' validate.errors = [ { keyword: \'' + ('$ref') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'can\\\'t resolve reference ' + ($schema) + '\' ';
if (it.opts.verbose) {
out += ', schema: \'' + ($schema) + '\', data: ' + ($data);
}
out += ' }]; return false; ';
} else {
out += ' var err = { keyword: \'' + ('$ref') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'can\\\'t resolve reference ' + ($schema) + '\' ';
if (it.opts.verbose) {
out += ', schema: \'' + ($schema) + '\', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
}
if ($breakOnError) {
out += ' if (false) { ';
}
} else {
var $v = 'v' + $lvl;
out += ' var ' + ($v) + ' = ' + ($refVal) + '; if (! ' + ($v) + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (validate.errors === null) validate.errors = ' + ($v) + '.errors; else validate.errors = validate.errors.concat(' + ($v) + '.errors); errors = validate.errors.length; } ';
if ($breakOnError) {
out += ' else { ';
}
}
}
return out;
}
3 changes: 3 additions & 0 deletions lib/dotjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
These files are compiled dot templates from dot folder.

Do NOT edit them directly, edit the templates and run `node bin/compile_dots` from main ajv folder.
40 changes: 40 additions & 0 deletions lib/dotjs/allOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';
module.exports = function anonymous(it) {
var out = ' ';
var $lvl = it.level,
$dataLvl = it.dataLevel,
$schema = it.schema['allOf'],
$schemaPath = it.schemaPath + '.' + 'allOf',
$breakOnError = !it.opts.allErrors;
var $data = 'data' + ($dataLvl || ''),
$valid = 'valid' + $lvl,
$errs = 'errs' + $lvl;
if (it.opts._debug) {
out += ' console.log(\'Keyword ' + ('allOf') + '\'); ';
}
var $it = it.util.copy(it),
$closingBraces = '';
$it.level++;
var arr1 = $schema;
if (arr1) {
var $sch, $i = -1,
l1 = arr1.length - 1;
while ($i < l1) {
$sch = arr1[$i += 1];
if (it.util.schemaHasRules($sch, it.RULES.all)) {
$it.schema = $sch;
$it.schemaPath = $schemaPath + '[' + $i + ']';
out += ' ' + (it.validate($it)) + ' ';
if ($breakOnError) {
out += ' if (valid' + ($it.level) + ') { ';
$closingBraces += '}';
}
}
}
}
if ($breakOnError) {
out += ' ' + ($closingBraces.slice(0, -1));
}
out = it.util.cleanUpCode(out);
return out;
}
46 changes: 46 additions & 0 deletions lib/dotjs/anyOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';
module.exports = function anonymous(it) {
var out = ' ';
var $lvl = it.level,
$dataLvl = it.dataLevel,
$schema = it.schema['anyOf'],
$schemaPath = it.schemaPath + '.' + 'anyOf',
$breakOnError = !it.opts.allErrors;
var $data = 'data' + ($dataLvl || ''),
$valid = 'valid' + $lvl,
$errs = 'errs' + $lvl;
if (it.opts._debug) {
out += ' console.log(\'Keyword ' + ('anyOf') + '\'); ';
}
var $it = it.util.copy(it),
$closingBraces = '';
$it.level++;
var $noEmptySchema = $schema.every(function($sch) {
return it.util.schemaHasRules($sch, it.RULES.all);
});
if ($noEmptySchema) {
out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = false; ';
var arr1 = $schema;
if (arr1) {
var $sch, $i = -1,
l1 = arr1.length - 1;
while ($i < l1) {
$sch = arr1[$i += 1];
$it.schema = $sch;
$it.schemaPath = $schemaPath + '[' + $i + ']';
out += ' ' + (it.validate($it)) + ' ' + ($valid) + ' = ' + ($valid) + ' || valid' + ($it.level) + '; if (!' + ($valid) + ') { ';
$closingBraces += '}';
}
}
out += ' ' + ($closingBraces) + ' if (' + ($valid) + ') { errors = ' + ($errs) + '; if (validate.errors !== null) { if (' + ($errs) + ') validate.errors.length = ' + ($errs) + '; else validate.errors = null; } ';
if (it.opts.allErrors) {
out += ' } ';
}
out = it.util.cleanUpCode(out);
} else {
if ($breakOnError) {
out += ' if (true) { ';
}
}
return out;
}
93 changes: 93 additions & 0 deletions lib/dotjs/dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict';
module.exports = function anonymous(it) {
var out = ' ';
var $lvl = it.level,
$dataLvl = it.dataLevel,
$schema = it.schema['dependencies'],
$schemaPath = it.schemaPath + '.' + 'dependencies',
$breakOnError = !it.opts.allErrors;
var $data = 'data' + ($dataLvl || ''),
$valid = 'valid' + $lvl,
$errs = 'errs' + $lvl;
if (it.opts._debug) {
out += ' console.log(\'Keyword ' + ('dependencies') + '\'); ';
}
var $it = it.util.copy(it),
$closingBraces = '';
$it.level++;
var $schemaDeps = {},
$propertyDeps = {};
for ($property in $schema) {
var $sch = $schema[$property];
var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps;
$deps[$property] = $sch;
}
out += 'var ' + ($errs) + ' = errors;';
for (var $property in $propertyDeps) {
out += ' if (' + ($data) + (it.util.getProperty($property)) + ' !== undefined) { ';
$deps = $propertyDeps[$property];
out += ' if ( ';
var arr1 = $deps;
if (arr1) {
var $dep, $i = -1,
l1 = arr1.length - 1;
while ($i < l1) {
$dep = arr1[$i += 1];
if ($i) {
out += ' || ';
}
out += ' ' + ($data) + (it.util.getProperty($dep)) + ' === undefined ';
}
}
out += ') { ';
if (it.wasTop && $breakOnError) {
out += ' validate.errors = [ { keyword: \'' + ('dependencies') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'';
if ($deps.length == 1) {
out += 'property ' + ($deps[0]) + ' is';
} else {
out += 'properties ' + ($deps.join(", ")) + ' are';
}
out += ' required when property ' + ($property) + ' is present\' ';
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }]; return false; ';
} else {
out += ' var err = { keyword: \'' + ('dependencies') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'';
if ($deps.length == 1) {
out += 'property ' + ($deps[0]) + ' is';
} else {
out += 'properties ' + ($deps.join(", ")) + ' are';
}
out += ' required when property ' + ($property) + ' is present\' ';
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
}
out += ' } ';
if ($breakOnError) {
$closingBraces += '}';
out += ' else { ';
}
out += ' }';
}
for (var $property in $schemaDeps) {
var $sch = $schemaDeps[$property];
if (it.util.schemaHasRules($sch, it.RULES.all)) {
out += ' valid' + ($it.level) + ' = true; if (' + ($data) + '[\'' + ($property) + '\'] !== undefined) { ';
$it.schema = $sch;
$it.schemaPath = $schemaPath + "['" + it.util.escapeQuotes($property) + "']";
out += ' ' + (it.validate($it)) + ' } ';
if ($breakOnError) {
out += ' if (valid' + ($it.level) + ') { ';
$closingBraces += '}';
}
}
}
if ($breakOnError) {
out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {';
}
out = it.util.cleanUpCode(out);
return out;
}
35 changes: 35 additions & 0 deletions lib/dotjs/enum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';
module.exports = function anonymous(it) {
var out = ' ';
var $lvl = it.level,
$dataLvl = it.dataLevel,
$schema = it.schema['enum'],
$schemaPath = it.schemaPath + '.' + 'enum',
$breakOnError = !it.opts.allErrors;
var $data = 'data' + ($dataLvl || ''),
$valid = 'valid' + $lvl,
$errs = 'errs' + $lvl;
if (it.opts._debug) {
out += ' console.log(\'Keyword ' + ('enum') + '\'); ';
}
var $i = 'i' + $lvl;
out += 'var enumSchema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ' , ' + ($valid) + ' = false;for (var ' + ($i) + '=0; ' + ($i) + '<enumSchema' + ($lvl) + '.length; ' + ($i) + '++) if (equal(' + ($data) + ', enumSchema' + ($lvl) + '[' + ($i) + '])) { ' + ($valid) + ' = true; break; } if (!' + ($valid) + ') { ';
if (it.wasTop && $breakOnError) {
out += ' validate.errors = [ { keyword: \'' + ('enum') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'should be equal to one of values\' ';
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }]; return false; ';
} else {
out += ' var err = { keyword: \'' + ('enum') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'should be equal to one of values\' ';
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
}
out += ' }';
if ($breakOnError) {
out += ' else { ';
}
return out;
}
Loading

0 comments on commit 5f36f17

Please sign in to comment.