'use strict'; module.exports = { setup: setupAsync, compile: compileAsync }; var util = require('./compile/util'); var ASYNC = { '*': checkGenerators, 'co*': checkGenerators, 'es7': checkAsyncFunction }; var TRANSPILE = { 'nodent': getNodent, 'regenerator': getRegenerator }; var MODES = [ { async: 'co*' }, { async: 'es7', transpile: 'nodent' }, { async: 'co*', transpile: 'regenerator' } ]; var regenerator, nodent; function setupAsync(opts, required) { if (required !== false) required = true; var async = opts.async , transpile = opts.transpile , check; switch (typeof transpile) { case 'string': var get = TRANSPILE[transpile]; if (!get) throw new Error('bad transpiler: ' + transpile); return (opts._transpileFunc = get(opts, required)); case 'undefined': case 'boolean': if (typeof async == 'string') { check = ASYNC[async]; if (!check) throw new Error('bad async mode: ' + async); return (opts.transpile = check(opts, required)); } for (var i=0; i<MODES.length; i++) { var _opts = MODES[i]; if (setupAsync(_opts, false)) { util.copy(_opts, opts); return opts.transpile; } } /* istanbul ignore next */ throw new Error('generators, nodent and regenerator are not available'); case 'function': return (opts._transpileFunc = opts.transpile); default: throw new Error('bad transpiler: ' + transpile); } } function checkGenerators(opts, required) { /* jshint evil: true */ try { (new Function('(function*(){})()'))(); return true; } catch(e) { /* istanbul ignore next */ if (required) throw new Error('generators not supported'); } } function checkAsyncFunction(opts, required) { /* jshint evil: true */ try { (new Function('(async function(){})()'))(); /* istanbul ignore next */ return true; } catch(e) { if (required) throw new Error('es7 async functions not supported'); } } function getRegenerator(opts, required) { try { if (!regenerator) { var name = 'regenerator'; regenerator = require(name); regenerator.runtime(); } if (!opts.async || opts.async === true) opts.async = 'es7'; return regeneratorTranspile; } catch(e) { /* istanbul ignore next */ if (required) throw new Error('regenerator not available'); } } function regeneratorTranspile(code) { return regenerator.compile(code).code; } function getNodent(opts, required) { /* jshint evil: true */ try { if (!nodent) { var name = 'nodent'; nodent = require(name)({ log: false, dontInstallRequireHook: true }); } if (opts.async != 'es7') { if (opts.async && opts.async !== true) console.warn('nodent transpiles only es7 async functions'); opts.async = 'es7'; } return nodentTranspile; } catch(e) { /* istanbul ignore next */ if (required) throw new Error('nodent not available'); } } function nodentTranspile(code) { return nodent.compile(code, '', { promises: true, sourcemap: false }).code; } /** * Creates validating function for passed schema with asynchronous loading of missing schemas. * `loadSchema` option should be a function that accepts schema uri and node-style callback. * @this Ajv * @param {Object} schema schema object * @param {Function} callback node-style callback, it is always called with 2 parameters: error (or null) and validating function. */ function compileAsync(schema, callback) { /* eslint no-shadow: 0 */ /* jshint validthis: true */ var schemaObj; var self = this; try { schemaObj = this._addSchema(schema); } catch(e) { setTimeout(function() { callback(e); }); return; } if (schemaObj.validate) { setTimeout(function() { callback(null, schemaObj.validate); }); } else { if (typeof this._opts.loadSchema != 'function') throw new Error('options.loadSchema should be a function'); _compileAsync(schema, callback, true); } function _compileAsync(schema, callback, firstCall) { var validate; try { validate = self.compile(schema); } catch(e) { if (e.missingSchema) loadMissingSchema(e); else deferCallback(e); return; } deferCallback(null, validate); function loadMissingSchema(e) { var ref = e.missingSchema; if (self._refs[ref] || self._schemas[ref]) return callback(new Error('Schema ' + ref + ' is loaded but ' + e.missingRef + ' cannot be resolved')); var _callbacks = self._loadingSchemas[ref]; if (_callbacks) { if (typeof _callbacks == 'function') self._loadingSchemas[ref] = [_callbacks, schemaLoaded]; else _callbacks[_callbacks.length] = schemaLoaded; } else { self._loadingSchemas[ref] = schemaLoaded; self._opts.loadSchema(ref, function (err, sch) { var _callbacks = self._loadingSchemas[ref]; delete self._loadingSchemas[ref]; if (typeof _callbacks == 'function') { _callbacks(err, sch); } else { for (var i=0; i<_callbacks.length; i++) _callbacks[i](err, sch); } }); } function schemaLoaded(err, sch) { if (err) return callback(err); if (!(self._refs[ref] || self._schemas[ref])) { try { self.addSchema(sch, ref); } catch(e) { callback(e); return; } } _compileAsync(schema, callback); } } function deferCallback(err, validate) { if (firstCall) setTimeout(function() { callback(err, validate); }); else return callback(err, validate); } } }