Reputation: 2773
There is a test.js:
const add = (x,y) => {
return x+y;
}
const multiply = (x,y,z) => {
return x*y*z;
}
I want to read this test.js from an index.js and print all its function name and arguments;
const fs = require("fs");
let file = fs.readFileSync("./test.js", "utf8");
let functionArg = "Do some operations"
console.log(functionArg)
//Result:
// add : [x,y]
// multiply : [x,y,z]
Without module.exports.
Is it possible to read js file and return all its function and their arguments.
Upvotes: 5
Views: 2985
Reputation: 1780
const
to this.
const
to this.
, all variables in test.js became the member of the instance. now you can interate this instance to get the members.Here is the code:
const fs = require("fs");
let file = fs.readFileSync("./test.js", "utf8");
const getFuncInfo = function(file) {
file = file.replace(new RegExp('const ', 'g'), 'this.');
eval(`function Container(){
${file}}
`)
const instance = new Container()
const names = Object.keys(instance)
return names.reduce((res, cur) => {
if(typeof instance[cur] == 'function') {
let args = instance[cur].toString()
res[cur] = args.split('(')[1].split(')')[0].split(',')
}
return res;
}, {})
}
let functionArg = getFuncInfo(file)
console.log(functionArg)
The result is:
{ add: [ 'x', 'y' ], multiply: [ 'x', 'y', 'z' ] }
Edit:
Regarding the question about what eval
does,
it is same as below:
const getFuncInfo = function(file) {
file = file.replace(new RegExp('const ', 'g'), 'this.');
// think like eval replace the content with below one
function Container(){
// content of test.js except `const ` is replaced with `this.`
this.add = (x,y) => {
return x+y;
}
this.multiply = (x,y,z) => {
return x*y*z;
}
}
// end of replacement
const instance = new Container()
const names = Object.keys(instance)
return names.reduce((res, cur) => {
if(typeof instance[cur] == 'function') {
let args = instance[cur].toString()
res[cur] = args.split('(')[1].split(')')[0].split(',')
}
return res;
}, {})
}
Upvotes: 1
Reputation: 5941
You can get functions and their arguments with the help of a JavaScript parser like esprima.
const fs = require("fs");
const esprima = require('esprima');
let file = fs.readFileSync("./test.js", "utf8");
let functionArg = esprima.parseScript(file);
functionArg.body.forEach(el => {
let variableDeclarator = el.declarations[0]
let params = []
variableDeclarator.init.params.forEach(arg => {
params.push(arg.name)
})
console.log(variableDeclarator.id.name, ' : ', [params.join()])
})
//Result:
// add : [ 'x,y' ]
// multiply : [ 'x,y,z' ]
Upvotes: 3
Reputation: 3019
When you need to read a code file, the better is to directly use a compiler when available.
It turns out acorn is a well known parser and you're probably already using it without knowing because you're probably using babel.
With acorn
you can parse a source file into an abstract source tree which in turn can be walked with acorn-walk
to find what you need.
example:
testfiletoparse.js
export function factorialR(n) {
if (n <= 1) {
return 1;
}
return factorialR(n - 1) * n;
}
export function bound(v, min, max) {
return Math.max(Math.min(v, min), max);
}
export function isNullOrEmpty(str) {
return !str || str.length === 0;
}
export const arrowFunction = v => v;
basicparser.js
import fs from 'fs';
const acorn = require('acorn');
const walk = require('acorn-walk');
require('acorn-object-rest-spread/inject')(acorn);
require('acorn-static-class-property-initializer/inject')(acorn);
require('acorn-class-fields/inject')(acorn);
const filePath = 'src/testfiletoparse.js';
const sourceCode = String(fs.readFileSync(filePath));
const program = acorn.parse(
sourceCode,
{
ranges: true,
locations: true,
sourceType: 'module',
plugins: {
objectRestSpread: true,
// es7: true,
staticClassPropertyInitializer: true,
classFields: true,
}
}
);
walk.full(
program,
/**
* @param {}
*/
(node) => {
if (node.type === 'FunctionDeclaration') {
console.log(`There's a FunctionDeclaration node at ${JSON.stringify(node.loc.start)}`);
console.log(`Function name is ${node.id.name}`);
const params = node.params.map((param) => {
return param.name;
}).join();
console.log(`Function params are ${params}`);
}
// it is a little tricky to catch arrow functions but trial and error will get you through it
if (node.type === 'VariableDeclarator' && node.init.type === 'ArrowFunctionExpression') {
console.log(`There's an arrow function expression declaration node at ${JSON.stringify(node.loc.start)}`);
console.log(`Its name is ${node.id.name}`);
const params = node.init.params.map((param) => {
return param.name;
}).join();
console.log(`Function params are ${params}`);
}
}
);
output
There's a FunctionDeclaration node at {"line":1,"column":7}
Function name is factorialR
Function params are n
There's a FunctionDeclaration node at {"line":8,"column":7}
Function name is bound
Function params are v,min,max
There's a FunctionDeclaration node at {"line":12,"column":7}
Function name is isNullOrEmpty
Function params are str
There's an arrow function expression declaration node at {"line":16,"column":13}
Its name is arrowFunction
Function params are v
Starting with this it should be quite straightforward to find a solution to your problem.
Upvotes: 3
Reputation: 1
I think you are on the right path already but you never used the file you read, it should actually be(I hope I got your question right):
let file = fs.readFileSync("./test.js", "utf8");
console.log(file);
// Result:
//const add = (x,y) => {
// return x+y;
//}
//const multiply = (x,y,z) => {
// return x*y*z;
// }
Upvotes: -3