Skip to content

Commit

Permalink
Merge branch 'ajv-v5-keywords'
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Dec 22, 2016
2 parents 499bb4c + b9a9ab8 commit 145f58c
Show file tree
Hide file tree
Showing 36 changed files with 2,593 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ node_modules

# Optional REPL history
.node_repl_history

# Compiled templates
keywords/dotjs/*.js

.DS_Store
6 changes: 4 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ module.exports = defineKeywords;
* Defines one or several keywords in ajv instance
* @param {Ajv} ajv validator instance
* @param {String|Array<String>|undefined} keyword keyword(s) to define
* @return {Ajv} ajv instance (for chaining)
*/
function defineKeywords(ajv, keyword) {
if (Array.isArray(keyword)) {
for (var i=0; i<keyword.length; i++)
get(keyword[i])(ajv);
return;
return ajv;
}
if (keyword) {
get(keyword)(ajv);
return;
return ajv;
}
for (keyword in KEYWORDS) get(keyword)(ajv);
return ajv;
}


Expand Down
92 changes: 92 additions & 0 deletions keywords/_formatLimit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'use strict';

var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i;
var DATE_TIME_SEPARATOR = /t|\s/i;

var COMPARE_FORMATS = {
date: compareDate,
time: compareTime,
'date-time': compareDateTime
};

module.exports = function (minMax) {
var keyword = 'format' + minMax;
return function defFunc(ajv) {
if (ajv.RULES.keywords[keyword])
return console.warn('Keyword', keyword, 'is already defined');

defFunc.definition = {
type: 'string',
inline: require('./dotjs/_formatLimit'),
statements: true,
errors: 'full',
metaSchema: {
anyOf: [
{ type: 'string' },
{
type: 'object',
required: [ '$data' ],
properties: {
$data: {
type: 'string',
anyOf: [
{ format: 'relative-json-pointer' },
{ format: 'json-pointer' }
]
}
},
additionalProperties: false
}
]
}
};

ajv.addKeyword(keyword, defFunc.definition);
ajv.addKeyword('formatExclusive' + minMax);
extendFormats(ajv);
return ajv;
};
};


function extendFormats(ajv) {
var formats = ajv._formats;
for (var name in COMPARE_FORMATS) {
var format = formats[name];
if (typeof format != 'object')
format = formats[name] = { validate: format };
if (!format.compare)
format.compare = COMPARE_FORMATS[name];
}
}


function compareDate(d1, d2) {
if (!(d1 && d2)) return;
if (d1 > d2) return 1;
if (d1 < d2) return -1;
if (d1 === d2) return 0;
}


function compareTime(t1, t2) {
if (!(t1 && t2)) return;
t1 = t1.match(TIME);
t2 = t2.match(TIME);
if (!(t1 && t2)) return;
t1 = t1[1] + t1[2] + t1[3] + (t1[4]||'');
t2 = t2[1] + t2[2] + t2[3] + (t2[4]||'');
if (t1 > t2) return 1;
if (t1 < t2) return -1;
if (t1 === t2) return 0;
}


function compareDateTime(dt1, dt2) {
if (!(dt1 && dt2)) return;
dt1 = dt1.split(DATE_TIME_SEPARATOR);
dt2 = dt2.split(DATE_TIME_SEPARATOR);
var res = compareDate(dt1[0], dt2[0]);
if (res === undefined) return;
return res || compareTime(dt1[1], dt2[1]);
}
116 changes: 116 additions & 0 deletions keywords/dot/_formatLimit.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}

var {{=$valid}} = undefined;

{{## def.skipFormatLimit:
{{=$valid}} = true;
{{ return out; }}
#}}

{{## def.compareFormat:
{{? $isData }}
if ({{=$schemaValue}} === undefined) {{=$valid}} = true;
else if (typeof {{=$schemaValue}} != 'string') {{=$valid}} = false;
else {
{{ $closingBraces += '}'; }}
{{?}}

{{? $isDataFormat }}
if (!{{=$compare}}) {{=$valid}} = true;
else {
{{ $closingBraces += '}'; }}
{{?}}

var {{=$result}} = {{=$compare}}({{=$data}}, {{# def.schemaValueQS }});

if ({{=$result}} === undefined) {{=$valid}} = false;
#}}


{{? it.opts.format === false }}{{# def.skipFormatLimit }}{{?}}

{{
var $schemaFormat = it.schema.format
, $isDataFormat = it.opts.v5 && $schemaFormat.$data
, $closingBraces = '';
}}

{{? $isDataFormat }}
{{
var $schemaValueFormat = it.util.getData($schemaFormat.$data, $dataLvl, it.dataPathArr)
, $format = 'format' + $lvl
, $compare = 'compare' + $lvl;
}}

var {{=$format}} = formats[{{=$schemaValueFormat}}]
, {{=$compare}} = {{=$format}} && {{=$format}}.compare;
{{??}}
{{ var $format = it.formats[$schemaFormat]; }}
{{? !($format && $format.compare) }}
{{# def.skipFormatLimit }}
{{?}}
{{ var $compare = 'formats' + it.util.getProperty($schemaFormat) + '.compare'; }}
{{?}}

{{
var $isMax = $keyword == 'formatMaximum'
, $exclusiveKeyword = 'formatExclusive' + ($isMax ? 'Maximum' : 'Minimum')
, $schemaExcl = it.schema[$exclusiveKeyword]
, $isDataExcl = it.opts.v5 && $schemaExcl && $schemaExcl.$data
, $op = $isMax ? '<' : '>'
, $result = 'result' + $lvl;
}}

{{# def.$data }}


{{? $isDataExcl }}
{{
var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr)
, $exclusive = 'exclusive' + $lvl
, $opExpr = 'op' + $lvl
, $opStr = '\' + ' + $opExpr + ' + \'';
}}
var schemaExcl{{=$lvl}} = {{=$schemaValueExcl}};
{{ $schemaValueExcl = 'schemaExcl' + $lvl; }}

if (typeof {{=$schemaValueExcl}} != 'boolean' && {{=$schemaValueExcl}} !== undefined) {
{{=$valid}} = false;
{{ var $errorKeyword = $exclusiveKeyword; }}
{{# def.error:'_formatExclusiveLimit' }}
}

{{# def.elseIfValid }}

{{# def.compareFormat }}
var {{=$exclusive}} = {{=$schemaValueExcl}} === true;

if ({{=$valid}} === undefined) {
{{=$valid}} = {{=$exclusive}}
? {{=$result}} {{=$op}} 0
: {{=$result}} {{=$op}}= 0;
}

if (!{{=$valid}}) var op{{=$lvl}} = {{=$exclusive}} ? '{{=$op}}' : '{{=$op}}=';
{{??}}
{{
var $exclusive = $schemaExcl === true
, $opStr = $op; /*used in error*/
if (!$exclusive) $opStr += '=';
var $opExpr = '\'' + $opStr + '\''; /*used in error*/
}}

{{# def.compareFormat }}

if ({{=$valid}} === undefined)
{{=$valid}} = {{=$result}} {{=$op}}{{?!$exclusive}}={{?}} 0;
{{?}}

{{= $closingBraces }}

if (!{{=$valid}}) {
{{ var $errorKeyword = $keyword; }}
{{# def.error:'_formatLimit' }}
}
28 changes: 28 additions & 0 deletions keywords/dot/patternRequired.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}

{{
var $key = 'key' + $lvl
, $matched = 'patternMatched' + $lvl
, $closingBraces = ''
, $ownProperties = it.opts.ownProperties;
}}

var {{=$valid}} = true;
{{~ $schema:$pProperty }}
var {{=$matched}} = false;
for (var {{=$key}} in {{=$data}}) {
{{# def.checkOwnProperty }}
{{=$matched}} = {{= it.usePattern($pProperty) }}.test({{=$key}});
if ({{=$matched}}) break;
}

{{ var $missingPattern = it.util.escapeQuotes($pProperty); }}
if (!{{=$matched}}) {
{{=$valid}} = false;
{{# def.addError:'patternRequired' }}
} {{# def.elseIfValid }}
{{~}}

{{= $closingBraces }}
73 changes: 73 additions & 0 deletions keywords/dot/switch.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.setupNextLevel }}


{{## def.validateIf:
{{# def.setCompositeRule }}
{{ $it.createErrors = false; }}
{{# def._validateSwitchRule:if }}
{{ $it.createErrors = true; }}
{{# def.resetCompositeRule }}
{{=$ifPassed}} = valid{{=$it.level}};
#}}

{{## def.validateThen:
{{? typeof $sch.then == 'boolean' }}
{{? $sch.then === false }}
{{# def.error:'switch' }}
{{?}}
var valid{{=$it.level}} = {{= $sch.then }};
{{??}}
{{# def._validateSwitchRule:then }}
{{?}}
#}}

{{## def._validateSwitchRule:_clause:
{{
$it.schema = $sch._clause;
$it.schemaPath = $schemaPath + '[' + $caseIndex + ']._clause';
$it.errSchemaPath = $errSchemaPath + '/' + $caseIndex + '/_clause';
}}
{{# def.insertSubschemaCode }}
#}}

{{## def.switchCase:
{{? $sch.if && {{# def.nonEmptySchema:$sch.if }} }}
var {{=$errs}} = errors;
{{# def.validateIf }}
if ({{=$ifPassed}}) {
{{# def.validateThen }}
} else {
{{# def.resetErrors }}
}
{{??}}
{{=$ifPassed}} = true;
{{# def.validateThen }}
{{?}}
#}}


{{
var $ifPassed = 'ifPassed' + it.level
, $currentBaseId = $it.baseId
, $shouldContinue;
}}
var {{=$ifPassed}};

{{~ $schema:$sch:$caseIndex }}
{{? $caseIndex && !$shouldContinue }}
if (!{{=$ifPassed}}) {
{{ $closingBraces+= '}'; }}
{{?}}

{{# def.switchCase }}
{{ $shouldContinue = $sch.continue }}
{{~}}

{{= $closingBraces }}

var {{=$valid}} = valid{{=$it.level}};

{{# def.cleanUp }}
3 changes: 3 additions & 0 deletions keywords/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 `npm run build` from main ajv-keywords folder.
1 change: 1 addition & 0 deletions keywords/dynamicDefaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ module.exports = function defFunc(ajv) {
};

ajv.addKeyword('dynamicDefaults', defFunc.definition);
return ajv;

function getDefault(d) {
var def = DEFAULTS[d];
Expand Down
3 changes: 3 additions & 0 deletions keywords/formatMaximum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('./_formatLimit')('Maximum');
3 changes: 3 additions & 0 deletions keywords/formatMinimum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('./_formatLimit')('Minimum');
6 changes: 5 additions & 1 deletion keywords/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ module.exports = {
range: require('./range'),
regexp: require('./regexp'),
'typeof': require('./typeof'),
dynamicDefaults: require('./dynamicDefaults')
dynamicDefaults: require('./dynamicDefaults'),
formatMinimum: require('./formatMinimum'),
formatMaximum: require('./formatMaximum'),
patternRequired: require('./patternRequired'),
'switch': require('./switch')
};
1 change: 1 addition & 0 deletions keywords/instanceof.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = function defFunc(ajv) {
};

ajv.addKeyword('instanceof', defFunc.definition);
return ajv;

function getConstructor(c) {
var Constructor = CONSTRUCTORS[c];
Expand Down

0 comments on commit 145f58c

Please sign in to comment.