tdewolff / parse

Go parsers for web formats
MIT License
413 stars 65 forks source link

JS parser: get the expression of a declared variable. #95

Closed defensadev closed 2 years ago

defensadev commented 2 years ago

I would like to know how one gets the value of a variable, and if possible the arguments of the function. For example I have the following JS code

JS Code

const App = ({ ticker, assetPair }) => {
  // code here
};

I ideally would like to extract the strings "ticker" and "assetPair" but just being able to get the expression (the arrow function) would be enough for me.

Go parser

ast, err := js.Parse(code, js.Options{})

// loop through each part of the script body
for _, body_el: = range ast.List {

    // get the type of each statement in the body
    switch body_type: = body_el.(type) {

        // we are looking for a variable declaration
        case *js.VarDecl:

            // make sure that the variable name is "App" like in the JS code
            if len(body_type.List) > 0 && body_type.List[0].Binding.String() == "App" {

                // not sure what to do here, how do I get the value / expression "App" is set to???
                switch body_type.List[0].Binding.(type) {
                    case *js.Var:
                        fmt.Println(body_el.String())
                }
            }
    }
}
defensadev commented 2 years ago

Found a hacky solution, but still would be interested in the proper way to do this

type walker struct {
    is_func_decl bool
    func_decl    *js.FuncDecl
    arrow_func   *js.ArrowFunc
}

func (w *walker) Enter(n js.INode) js.IVisitor {
    switch n := n.(type) {
    case *js.FuncDecl:
        if w.func_decl == nil && w.arrow_func == nil {
            w.is_func_decl = true
            w.func_decl = n
        }
    case *js.ArrowFunc:
        if w.func_decl == nil && w.arrow_func == nil {
            w.is_func_decl = false
            w.arrow_func = n
        }
    }

    return w
}

func (w *walker) Exit(n js.INode) {}

func main() {
ast, err := js.Parse(code, js.Options{})

// loop through each part of the script body
for _, body_el: = range ast.List {

    // get the type of each statement in the body
    switch body_type: = body_el.(type) {

        // we are looking for a variable declaration
        case *js.VarDecl:

            // make sure that the variable name is "App" like in the JS code
            if len(body_type.List) > 0 && body_type.List[0].Binding.String() == "App" {
                 w := walker{}
                 js.walk(&w, body_type)
                 // if there was a function decl or arrow function decl
                 // walker now contains it.
                 w.func_decl 
                 w.arrow_func 
                 w.is_func_decl 
            }
    }
}
}
tdewolff commented 2 years ago

In body_type.List[0].Default will be the expression that corresponds to the arrow function, see https://github.com/tdewolff/parse/blob/master/js/ast.go#L1122. To get the variable names inside the arrow function parameter list, do the following:

if fun, ok := body_type.List[0].Default.(*js.ArrowFunc); ok {
    for _, param := range fun.Params.List {
        if v, ok := param.Binding.(*js.Var); ok {
            fmt.Println(v)
        }
    }
}
defensadev commented 2 years ago

Thanks man! Looks easier than I thought!