219 lines
5.6 KiB
JavaScript
219 lines
5.6 KiB
JavaScript
|
'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);
|
||
|
}
|
||
|
}
|
||
|
}
|