如何用刚刚解析的javascript(字符串)替换AST中的路径?

时间:2018-04-23 21:37:05

标签: abstract-syntax-tree jscodeshift codemod

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

我现在发现我正试图replace表达我并不关心其中的内容。

在此示例中,我在

中找到了this.state.showMenu && this.handleMouseDown部分
<a
  onMouseDown={this.state.showMenu && this.handleMouseDown}
>

我需要转换为:

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

如何在不明确重建树的情况下这样做?我只是想做一些像

这样的事情
path.replaceText("this.state.showMenu ? this.handleMouseDown : undefined")

2 个答案:

答案 0 :(得分:2)

这是一个执行你所描述的变换器:

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()
}

在行动here中查看。

您也可以在JSXExpressionContainer节点中放置任意文本:

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()
}

请参阅this example

最后,您甚至不需要返回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()
}

查看结果here

答案 1 :(得分:1)

您可以使用我们的DMS Software Reengineering Toolkit.

执行此操作

DMS将H​​TML页面视为具有嵌入式脚本子语言的本机HTML文本,可能是ECMAScript,VBScript或其他内容。 所以建立一个完整的HTML&#34; AST&#34;首先需要一个 构建纯HTML部分,然后找到所有&#34; onXXXXX&#34;标签并使用所选的脚本语言将它们转换为AST。 DMS可以区分AST节点和不同的语言,因此在理解化合物AST时没有混淆的可能性。

因此,首先我们需要解析感兴趣的HTML文档(由于教学原因而编辑的代码):

(local (;; [my_HTML_AST AST:Node]
           (includeunique `DMS/Domains/HTML/Component/ParserComponent.par')
        );;
     (= working_graph (AST:CreateForest))
     (= my_HTML_AST (Parser:ParseFile parser working_graph input_file_full_path))

然后我们需要遍历HTML树,找到JavaScript文本片段,解析它们并拼接解析的ECMASCript树以替换文本片段:

(local (;; (includeunique `DMS/Domains/ECMAScript/Components/ParserComponent.par') );;
       (ScanNodes my_HTML_AST
            (lambda (function boolean AST:Node)
                  (ifthenelse (!! (~= (AST:GetNodeType ?) GrammarConstants:Rule:Attribute) ; not an HTML attribute
                                  (~= (Strings:Prefix (AST:GetLiteralString (AST:GetFirstChild ?)) `on')) ; not at action attribute
                               )&&
                      ~t ; scan deeper into tree
                      (value (local (;; [my_ECMAScript_AST AST:Node]
                                        [ECMASCript_text_stream streams:buffer]
                                    );;
                                 (= ECMAScript_text_stream (InputStream:MakeBufferStream (AST:StringLiteral (AST:GetSecondChild ?))=
                                 (= my_ECMAScript_AST (Parser:ParseStream parser working_graph ECMAScript_text_stream))
                                 (= AST:ReplaceNode ? my_ECMAScript_AST)
                                 (= InputStream:Close my_ECMAScript_text_stream)
                         ~f) ; no need to scan deeper here
                  )ifthenelse
            )lambda
       ) ; at this point, we have a mixed HTML/ECMAScript tree
)local

如果脚本语言可能是其他内容,则此代码必须更改。如果你的页面都是HTML + ECMAScript,你可以将上面的东西包装成一个黑盒子并称之为#34;(ParseHTML)&#34;这是另一个答案所假设的事情。

现在进行实际工作。 OP希望将HTML中的模式替换为另一种模式。这里的DMS很闪耀,因为您可以使用目标语言的语法直接将这些模式编写为DMS重写规则(请参阅this link for details)。

source domain ECMAScript;
target domain ECMAScript;
rule OP_special_rewrite()=expression -> expression
     "this.state.showMenu && this.handleMouseDown"
  ->  "this.state.showMenu ? this.handleMouseDown : undefined "

现在你需要应用这个重写:

(RSL:Apply my_HTML_AST `OP_special_rewrite') ; applies this rule to every node in AST
; only those that match get modified

最后从AST重新生成文本:

 (PrettyPrinter:PrintStream my_ECMAScript_AST input_file_full_path)
OP的例子非常简单,因为他匹配的是一个恒定模式。 DMS的规则可以使用各种模式变量编写;见上面的链接,并且可以对匹配的模式和其他状态信息有任意条件来控制规则是否适用。