API-интерфейс компилятора TypeScript теряет форматирование во время преобразования - PullRequest
0 голосов
/ 27 ноября 2018

Мне нужно изменить около 1000 файлов машинописного текста особым образом: мне нужно заменить все StringLiteral и JsxText токены на CallExpression на функцию перевода для целей интернационализации моего приложения.

Я уже выполнил такую ​​задачу с помощью нашей кодовой базы C # с Roslyn, поэтому сейчас я пытаюсь выполнить аналогичную задачу с помощью API компилятора машинописи.Это очень похоже на API Roslyn, но у них есть неприятная разница.В Roslyn у вас есть понятие токенов викторины: таких, которые не излучают ничего интересного, но необходимы для удобства чтения.Это пробелы, вкладки, комментарии и т. Д. В синтаксическом дереве Roslyn у вас есть все мелочи из вашего исходного файла.Когда вы каким-то образом меняете синтаксическое дерево C # и отправляете исходный код из этого синтаксического дерева, у вас все то же форматирование, комментарии, пробелы и все такое.

К сожалению, в машинописном AST нет никаких маркеров пустяков, поэтому, когда я использую такой код, все мое форматирование исчезает.

const result: ts.TransformationResult<ts.SourceFile> = ts.transform(
  sourceFile, [ transformerFactory(visitorFunction) ]
);

const transformedSourceFile: ts.SourceFile = result.transformed[0];

const printer: ts.Printer = ts.createPrinter();

const generated: string = printer.printNode( ts.EmitHint.SourceFile, transformedSourceFile, sourceFile);

Какие варианты у меня есть?

  1. Я могу придерживаться описанного выше подхода, но это приведет к большому количеству бесполезного редактирования, испорченногоGitHub история и гигантский запрос тянуть.При таком подходе я должен определенно использовать Prettier после моих преобразований и, вероятно, мне следует установить его как зависимость разработчика и в наш CI, чтобы у нас не было таких проблем в будущем.
  2. Я могувсе еще использую AST для обнаружения моих токенов, но я могу сделать преобразование без ts.Printer и ts.Transformation.Я могу заставить все литералы обрабатывать на этапе обнаружения, упорядочивать их по убыванию в файле и заменять их, используя substring или что-то вроде этогоЭто довольно сложная вещь, и я не хочу этого делать, но я не доволен недостатками первого варианта.

Так что мне делать?У меня есть другие варианты?

1 Ответ

0 голосов
/ 21 января 2019

Вы можете использовать инструменты, которые захватывают форматирование и комментарии, и регенерируют их по завершении процесса преобразования, как вы заметили, что делает Roslyn.Однако Roslyn и «компилятор» TypeScript специфичны для их целевых языков.

В общем, вам нужна «система преобразования программ».Это инструменты, которые принимают грамматики, автоматически создают AST, которые захватывают все эти данные форматирования, позволяют вам определять преобразования с использованием шаблонов уровня источника и выполнять эти преобразования путем сопоставления / исправления AST, и они просто распечатывают модифицированное дерево, сохраняющее эти данные форматирования.

Наш комплект реинжиниринга программного обеспечения DMS может сделать это.

Для этого нужно определить грамматику целевого языка;мы сделали для многих языков, включая JavaScript, но еще не для TypeScript.Однако вы можете строить языковые диалекты, опираясь на другие определения.Или вы можете сделать TypeScript с нуля;это не сложно, если у вас есть явная грамматика, которая, я думаю, существует для TypeScript.Часть этого определения сообщает синтаксическому анализу, как распознать комментарии, чтобы их можно было сохранить;DMS знает, как сохранить все данные форматирования и макета.

Таким образом, для решения конкретной задачи вы могли бы написать на самом деле очень простые преобразования, используя правила перезаписи DMS:

source domain ECMAScript~TypeScript; -- assuming TypeScript is built as a dialect
target domain ECMAScript~TypeScript; -- we're defining rules that map TypeScript to itself
    -- you could write rules map TypeScript to C++ if you insist

rule InternationalizeStringLiteral(s:STRINGLITERAL): primary-> primary
  = "\s"-> "Translate(\s)";

rule InternationalizeJsText(jst:JSTText): primary -> primary
  = " \jst " -> "Translate(\jst)";

ruleset Internationalize = { InternationalizeStringLiteral, InternationalizeJsText};

Вы можетепопросите DMS проанализировать файл, применить нижний набор правил к вашему дереву, а затем распечатать результат.

Эти правила полностью поддерживают синтаксис, поскольку они работают с AST, поэтому их не обманывает текст в комментариях.или строковые литералы, или границы строк / пробелы / форматы / переплетенные комментарии, ...

Теперь у вас есть 1000 файлов для изменения.Это достаточно большой, поэтому может стоить усилий для определения TypeScript и применения DMS.(Если бы интерфейсная часть TypeScript для DMS была готова, было бы хлопотать, сделайте все выше).Иногда это не так;YMMV в зависимости от того, что вы действительно хотите сделать.DMS лучше всего использовать на больших базах кода, и он действительно хорош, если вам нужно выполнить сложные преобразования.

...