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

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

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")

这是一个可以按照您的描述进行操作的转换器:

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

最后,您甚至不需要 return 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

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

DMS 将 HTML 页面视为带有嵌入式脚本子语言的原生 HTML 文本,这些子语言可能是 ECMAScript、VBScript 或其他语言。 所以构建一个完整的 HTML "AST" 的过程首先需要一个 构建纯 HTML 部分,然后找到所有 "onXXXXX" 标记并将它们转换为所选脚本语言的 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,你可以将上面的东西包装到一个黑盒子里并称之为“(ParseHTML)”,这是其他答案假设发生的。

现在开始实际工作。 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的规则可以使用各种模式变量来编写;参见上文 link,并且可以对匹配模式和其他状态信息设置任意条件以控制规则是否适用。