Как заменить путь в AST только что проанализированным JavaScript(строка)?

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 ответа

Решение

Вот трансформер, который делает то, что вы описываете:

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

Смотрите это в действии здесь.

Вы также можете просто поместить произвольный текст в 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()
}

Смотрите этот пример.

Наконец, вам даже не нужно возвращать 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()
}

Смотрите результат здесь.

Вы можете сделать это с помощью нашего 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)", как и предполагалось в другом ответе.

Теперь о фактической работе. ОП хочет заменить шаблон, найденный в его HTML, на другой. Здесь DMS сияет, потому что вы можете написать эти шаблоны, используя синтаксис целевого языка, непосредственно как правило перезаписи DMS (подробности см. По этой ссылке).

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 могут быть написаны с использованием всех видов шаблонных переменных; см. ссылку выше, и может иметь произвольные условия для сопоставленного шаблона и другой информации о состоянии, чтобы контролировать применимость правила.

Другие вопросы по тегам