abstract-syntax-treejscodeshiftcodemod

How to replace a path in AST with just parsed javascript(string)?


https://astexplorer.net/#/gist/70df1bc56b9ee73d19fc949d2ef829ed/7e14217fd8510f0bf83f3372bf08454b7617bce1

I've found now I'm trying to replace an expression and I don't care whats in it.

in this example I've found the this.state.showMenu && this.handleMouseDown portion in

<a
  onMouseDown={this.state.showMenu && this.handleMouseDown}
>

I need to convert to:

<a
  onMouseDown={this.state.showMenu ? this.handleMouseDown : undefined}
>

how can I do so without explicitly reconstructing the tree? I just want to do something like

path.replaceText("this.state.showMenu ? this.handleMouseDown : undefined")

Solution

  • Here's a transformer that does what you describe:

    export default function transformer(file, api) {
      const j = api.jscodeshift;
      const root = j(file.source)
    
      root
        .find(j.JSXExpressionContainer)
        .replaceWith(path => {
            return j.jsxExpressionContainer(
                j.conditionalExpression(
                    j.identifier(j(path.value.expression.left).toSource()),
                    j.identifier(j(path.value.expression.right).toSource()),
                    j.identifier('undefined')
                )
            )
        })
    
      return root.toSource()
    }
    

    See it in action here.

    You can also just put arbitrary text in the JSXExpressionContainer node:

    export default function transformer(file, api) {
      const j = api.jscodeshift;
      const root = j(file.source)
    
      root
        .find(j.JSXExpressionContainer)
        .replaceWith(path => {
            return j.jsxExpressionContainer(
                j.identifier('whatever you want')
            )
        })
    
      return root.toSource()
    }
    

    See this example.

    Finally, you don't even need to return a JSXExpressionContainer.

    export default function transformer(file, api) {
      const j = api.jscodeshift;
      const root = j(file.source)
    
      root
        .find(j.JSXExpressionContainer)
        .replaceWith(path => {
            return j.identifier("this isn't valid JS, but works just fine")
        })
    
      return root.toSource()
    }
    

    See the result here.