babel-plugin-runtyper
Version:
Babel plugin for runtime type-checking in JavaScript
100 lines (84 loc) • 2.46 kB
JavaScript
/**
* Base class for all binary assertions
*/
'use strict';
const t = require('babel-types');
const template = require('babel-template');
const utils = require('../utils');
const WRAPPER_TPL = `
(function NAME(a, b) {
var t = ${utils.typeInfo.toString()};
var ta = t(a, CUSTOM_TYPES);
var tb = t(b, CUSTOM_TYPES);
var msg = '';
ASSERTIONS
if (msg) {
var s = ${utils.valueInfo.toString()};
msg += ': ' + s(a, ta) + ' ' + OPERATOR + ' ' + s(b, tb);
NOTIFY;
}
return RESULT;
})(PARAM1, PARAM2)
`;
const LEVEL_NOTIFY = {
'info': 'console.info(msg)',
'warn': 'console.warn(new Error(msg))',
'error': 'console.error(new Error(msg))',
'break': 'throw new Error(msg)',
};
module.exports = class BaseAssertion {
constructor(options, OPERATORS) {
this._options = options;
this._operators = OPERATORS;
this._customTypes = 0;
this._tpl = null;
}
tryReplace(path) {
this._setPath(path);
if (this._needReplace()) {
this._replace();
return true;
}
}
_buildTpl(assertionsTpl) {
const wrappedTpl = WRAPPER_TPL.trim()
.replace('ASSERTIONS', assertionsTpl.trim())
.replace('NOTIFY', LEVEL_NOTIFY[this._options.warnLevel])
.replace(/CUSTOM_TYPES/g, this._customTypes);
this._tpl = template(wrappedTpl);
}
_setPath(path) {
this._path = path;
this._operator = this._path.node.operator;
this._left = this._path.node.left;
this._right = this._path.node.right;
}
_needReplace() {
return this._isSuitableOperator() && this._hasImplicitOperands();
}
_replace() {
const replacement = this._tpl({
NAME: t.identifier(this._getFunctionName()),
OPERATOR: t.stringLiteral(this._operator),
PARAM1: this._left,
PARAM2: this._right,
RESULT: t.binaryExpression(this._operator, t.identifier('a'), t.identifier('b'))
});
this._path.replaceWith(replacement);
}
_getFunctionName() {
return this._operators[this._operator];
}
_isSuitableOperator() {
return this._operators.hasOwnProperty(this._operator);
}
_hasImplicitOperands() {
return !isExplicitValue(this._left) || !isExplicitValue(this._right);
}
};
function isExplicitValue(node) {
return t.isLiteral(node) || isExplicitBinaryExpression(node);
}
function isExplicitBinaryExpression(node) {
return t.isBinaryExpression(node) && isExplicitValue(node.left) && isExplicitValue(node.right);
}