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
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");
|
|
} |