Shubham Chadokar
Shubham Chadokar

Reputation: 2773

How to get all the function and their arguments from a js file? javascript

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

Answers (4)

wang
wang

Reputation: 1780

  • read a js file
  • replace const to this.
  • wrap it inside constructor function and evaluate it.
  • create a instance of it
  • since you replaced const to this., all variables in test.js became the member of the instance. now you can interate this instance to get the members.
  • to get the function signature, you will have to convert a function object to string and get the arguments manually.

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

TGrif
TGrif

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

remix23
remix23

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

Steve S.
Steve S.

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

Related Questions