You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
3.8 KiB
JavaScript

"use strict";
let expressions = [
function foo({id, other_prop}) {
return (other_prop < 34 && other_prop >= 2 && id !== "foo");
},
(data) => {
let id = data.id;
let someProp = data.prop;
let otherProp = someProp;
if (id !== "foo") {
if (someProp < 34) {
return (otherProp >= 2);
} else {
return false;
}
}
},
({foo: bar}) => bar > 4,
function (data) {
let {foo} = data;
return (foo < 4);
},
42,
"bar"
];
const util = require("util");
const acorn = require("acorn");
const estreeAssignParent = require("estree-assign-parent");
const scopeAnalyzer = require("scope-analyzer");
const astw = require("astw");
function logAst(ast, depth = 7) {
return util.inspect(ast, {colors: true, depth: depth});
}
function analyzeFunction(func) {
let source = func.toString();
console.log(source);
console.log("");
/* The below is a hack to make anonymous functions parse correctly. */
let ast = acorn.parse(`[${source}]`);
let functionAst = ast.body[0].expression.elements[0];
estreeAssignParent(functionAst);
scopeAnalyzer.crawl(functionAst);
let functionArg = functionAst.params[0];
if (functionArg.type === "ObjectPattern") {
for (let property of functionArg.properties) {
// key = from prop, value = to var
let sourcePropertyName = property.key.name;
property.value._sourceProp = sourcePropertyName;
}
} else if (functionArg.type === "Identifier") {
functionArg._dataSource = true;
} else {
throw new Error(`Encountered unrecognized function argument type: ${functionArg.type}`);
}
let walk = astw(functionAst);
walk((node) => {
if (node.type === "VariableDeclarator") {
if (node.id.type === "ObjectPattern") {
if (node.init.type === "Identifier" && scopeAnalyzer.getBinding(node.init).definition._dataSource === true) {
for (let property of node.id.properties) {
property.value._sourceProp = property.key.name;
}
}
} else if (node.id.type === "Identifier") {
if (node.init.type === "MemberExpression" && scopeAnalyzer.getBinding(node.init.object).definition._dataSource === true) {
node.id._sourceProp = node.init.property.name;
} else if (node.init.type === "Identifier") {
let definition = scopeAnalyzer.getBinding(node.init).definition;
if (definition._sourceProp != null) {
node.id._sourceProp = definition._sourceProp;
}
}
} else {
throw new Error(`Encountered unrecognized assignment id type: ${node.id.type}`);
}
}
});
let walkBody = astw(functionAst.body);
function getDataProperty(node) {
if (node.type === "Identifier" && node.parent.type !== "MemberExpression") {
let binding = scopeAnalyzer.getBinding(node);
if (binding != null) {
let definition = binding.definition;
if (definition != null) {
if (definition._dataSource !== true) {
if (definition._sourceProp != null) {
return definition._sourceProp;
} else {
throw new Error(`Could not connect variable '${node.name}' to a data property`);
}
}
} else {
throw new Error(`Encountered undefined variable: ${node.name}`);
}
} else {
throw new Error(`Encountered undefined variable: ${node.name}`);
}
} else {
throw new Error("Node is not a free-standing (variable) identifier");
}
}
walkBody((node) => {
if (node.type === "Identifier" && node.parent.type !== "MemberExpression") {
let sourceProperty = getDataProperty(node);
if (sourceProperty != null) {
console.log(`Variable '${node.name}' is derived from data property '${sourceProperty}'`);
}
}
});
// console.log(util.inspect(functionAst, {depth: 7, colors: true}));
}
for (let expression of expressions) {
if (typeof expression === "function") {
let result = analyzeFunction(expression);
// console.log(expression.toString());
} else {
console.log(`!! ${expression} is not a function`);
}
console.log("\n=\n");
}