metal-soy-critic
Version:
A metal-soy code validation utility.
119 lines (118 loc) • 4.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const babylon = require("babylon");
const T = require("babel-types");
const babel_traverse_1 = require("babel-traverse");
class JSContext {
constructor(raw) {
this.raw = raw;
this.ast = babylon.parse(raw, { allowImportExportEverywhere: true });
this._defaultBinding = this._getDefaultBinding();
}
static getNameOrMemberName(node) {
if (T.isIdentifier(node)) {
return node.name;
}
else if (T.isMemberExpression(node)) {
return JSContext.getKeyName(node.property);
}
return null;
}
static getKeyName(node) {
if (T.isIdentifier(node)) {
return node.name;
}
else if (T.isStringLiteral(node)) {
return node.value;
}
throw new Error('Unable to parse key name');
}
static hasAttribute(node, name) {
if (T.isIdentifier(node) && node.name === 'Config') {
return false;
}
if (T.isCallExpression(node) &&
T.isMemberExpression(node.callee) &&
T.isIdentifier(node.callee.property)) {
if (node.callee.property.name === name) {
return true;
}
return JSContext.hasAttribute(node.callee.object, name);
}
return false;
}
getClassMethodNames() {
const methodNames = [];
if (this._defaultBinding && T.isClassDeclaration(this._defaultBinding.path.node)) {
this._defaultBinding.path.node.body.body.forEach(node => {
if (T.isClassMethod(node)) {
methodNames.push(JSContext.getKeyName(node.key));
}
});
}
return methodNames;
}
getParentClassName() {
if (this._defaultBinding && T.isClassDeclaration(this._defaultBinding.path.node)) {
return JSContext.getNameOrMemberName(this._defaultBinding.path.node.superClass);
}
return null;
}
getParams() {
let params = [];
if (this._defaultBinding) {
for (let i = 0; i < this._defaultBinding.referencePaths.length; i++) {
const { parentPath } = this._defaultBinding.referencePaths[i];
if (T.isMemberExpression(parentPath.node) &&
T.isIdentifier(parentPath.node.property) &&
parentPath.node.property.name === 'STATE' &&
T.isAssignmentExpression(parentPath.parentPath.node) &&
T.isObjectExpression(parentPath.parentPath.node.right)) {
params = parentPath.parentPath.node.right.properties
.filter(node => T.isObjectProperty(node));
break;
}
}
}
return params;
}
getParamNames() {
return this.getParams()
.map(param => JSContext.getKeyName(param.key));
}
getSuperClassImportPath() {
let importPath = null;
const parentClassName = this.getParentClassName();
if (parentClassName) {
this.visit({
Program(path) {
const binding = path.scope.getBinding(parentClassName);
if (binding && T.isImportDeclaration(binding.path.parentPath.node)) {
importPath = binding.path.parentPath.node.source.value;
path.stop();
}
}
});
}
return importPath;
}
visit(traverseOptions) {
babel_traverse_1.default(this.ast, traverseOptions);
}
_getDefaultBinding() {
let binding = null;
this.visit({
ExportDefaultDeclaration(path) {
path.stop();
if (T.isIdentifier(path.node.declaration)) {
binding = path
.findParent(path => path.isProgram())
.scope
.getBinding(path.node.declaration.name);
}
}
});
return binding;
}
}
exports.default = JSContext;