@jesobreira/babel-plugin-transform-currency-operators
Version:
https://github.com/scurker/babel-plugin-transform-currency-operators
209 lines (163 loc) • 6.49 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var path = _interopDefault(require('path'));
var rootPath = process.cwd();
function resolveRelativePath(currentFile, importPath) {
var absolutePath = path.resolve(rootPath, importPath),
relativePathToImport = path.relative(path.dirname(currentFile), absolutePath);
// Check to see if path is relative to currentFile's folder
if (relativePathToImport.indexOf('../') !== 0) {
relativePathToImport = './' + relativePathToImport;
}
return relativePathToImport;
}
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var arithmeticOperators = {
'+': 'add',
'-': 'subtract',
'/': 'divide',
'*': 'multiply'
};
var compareOperators = ['===', '==', '>', '<', '>=', '<=', '!=', '!=='];
function transformCurrencyOperators(_ref) {
var t = _ref.types;
function expressionCallsCurrency(node, identifiers) {
if (!node) {
return false;
}
if (t.isBinaryExpression(node)) {
return expressionCallsCurrency(node.left, identifiers);
}
if (t.isCallExpression(node)) {
return expressionCallsCurrency(node.callee, identifiers);
}
if (t.isMemberExpression(node)) {
return expressionCallsCurrency(node.object, identifiers);
}
if (t.isFunctionExpression(node)) {
return expressionCallsCurrency(node.body.body.find(function (n) {
return t.isReturnStatement(n);
}), identifiers);
}
if (t.isArrowFunctionExpression(node)) {
if (t.isCallExpression(node.body)) {
return expressionCallsCurrency(node.body, identifiers);
} else if (t.isBlockStatement(node.body)) {
return expressionCallsCurrency(node.body.body.find(function (n) {
return t.isReturnStatement(n);
}), identifiers);
}
}
if (t.isReturnStatement(node)) {
return expressionCallsCurrency(node.argument.callee, identifiers);
}
return t.isIdentifier(node) && identifiers.has(node.name);
}
function pathContainsCurrency(refPath, methodName) {
var node = refPath.node,
currencyVariables = new Set([methodName]);
var Visitors = {
FunctionDeclaration: function FunctionDeclaration(_ref2) {
var node = _ref2.node;
if (expressionCallsCurrency(node.body.body.find(function (n) {
return t.isReturnStatement(n);
}), currencyVariables)) {
currencyVariables.add(node.id.name);
}
},
VariableDeclarator: function VariableDeclarator(_ref3) {
var scope = _ref3.scope,
node = _ref3.node;
if (expressionCallsCurrency(node.init, currencyVariables)) {
var binding = scope.bindings[node.id.name] || {};
if (binding.kind !== 'let' || binding.kind === 'let' && binding.scope === refPath.scope) {
currencyVariables.add(node.id.name);
}
}
},
AssignmentExpression: function AssignmentExpression(_ref4) {
var node = _ref4.node;
var left = node.left,
right = node.right;
if (!expressionCallsCurrency(right, currencyVariables)) {
currencyVariables.delete(left.name);
} else {
currencyVariables.add(left.name);
}
}
};
// Attempt to find any currency variables in scope
var currentPath = refPath.parentPath;
while (currentPath) {
currentPath.traverse(Visitors);
currentPath = currentPath.parentPath;
}
return node && expressionCallsCurrency(node, currencyVariables);
}
function buildExpression(path$$1, methodName) {
var node = path$$1.node,
operator = node.operator,
left = node.left,
right = node.right;
if (t.isBinaryExpression(left)) {
left = buildExpression(path$$1.get('left'));
}
var currencyMethod = arithmeticOperators[operator];
if (currencyMethod) {
return t.callExpression(t.memberExpression(t.isIdentifier(left) ? t.identifier(left.name) : left, t.identifier(currencyMethod)), [right]);
} else if (compareOperators.includes(operator) && !t.isMemberExpression(node.left)) {
return t.binaryExpression(operator, t.memberExpression(left, t.identifier('value')), pathContainsCurrency(path$$1.get('right'), methodName) ? t.memberExpression(right, t.identifier('value')) : right);
} else {
return node;
}
}
return {
pre: function pre(_ref5) {
var opts = _ref5.opts;
var filename = opts.filename,
paths = Array.isArray(this.opts) ? this.opts.map(function (path$$1) {
return resolveRelativePath(filename, path$$1);
}) : [];
this.currencyResolution = new Set(['currency.js'].concat(_toConsumableArray(paths)));
},
visitor: {
VariableDeclarator: function VariableDeclarator(_ref6, _ref7) {
var node = _ref6.node;
var opts = _ref7.opts,
currencyResolution = _ref7.currencyResolution;
var init = node.init;
if (t.isCallExpression(init) && init.callee.name === 'require' && currencyResolution.has(init.arguments[0].value)) {
opts.hasCurrency = true;
opts.methodName = node.id.name;
}
return;
},
ImportDeclaration: function ImportDeclaration(_ref8, _ref9) {
var node = _ref8.node;
var opts = _ref9.opts,
currencyResolution = _ref9.currencyResolution;
var source = node.source,
specifiers = node.specifiers;
if (currencyResolution.has(source.value)) {
var defaultImport = specifiers.find(function (specifier) {
return t.isImportDefaultSpecifier(specifier);
});
opts.hasCurrency = true;
opts.methodName = defaultImport.local.name;
}
return;
},
BinaryExpression: function BinaryExpression(path$$1, _ref10) {
var opts = _ref10.opts;
if (opts.hasCurrency && pathContainsCurrency(path$$1.get('left'), opts.methodName)) {
// Prevent replacement nodes from being visited multiple times
path$$1.stop();
path$$1.replaceWith(buildExpression(path$$1, opts.methodName));
return;
}
return;
}
}
};
}
module.exports = transformCurrencyOperators;