TypeScript Compiler API теряет форматирование во время преобразования

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

Я уже выполнил такую ​​задачу с нашей кодовой базой C# с помощью Roslyn, поэтому теперь я пытаюсь выполнить аналогичную задачу с помощью API компилятора машинописного текста. Он очень похож на API Roslyn, но у них есть неприятная разница. В Roslyn у вас есть понятие токенов Trivia: таких, которые не выделяют ничего интересного, но необходимы для удобства чтения. Это пробелы, табуляции, комментарии и т. д. В синтаксическом дереве 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 и гигантским запросам на перенос. При таком подходе я определенно должен использовать Красивее после моих преобразований и, вероятно, мне следует установить его как зависимость разработчика и в нашем CI, чтобы у нас не было таких проблем в будущем.
  2. Я все еще могу использовать AST для обнаружения своих токенов, но могу выполнять преобразование без ts.Printer и ts.Transformation. Я могу заставить все литералы обрабатывать на этапе обнаружения, упорядочить их по положению в файле по убыванию и заменить их с помощью substring или чего-то в этом роде. Это довольно сложная вещь, и я не очень хочу этим заниматься, но я не доволен недостатками первого варианта.

И что я должен делать? Есть ли у меня другие варианты?

TypeScript фактически сохраняет понятие мелочей, я думаю, это просто не так четко представлено в API. Вы можете посмотреть: basarat.gitbook.io/typescript/overview/ast/…. Вот пример, в котором вы можете видеть, что AST сохраняет мелочи, но вам нужно вручную получить их: ts-ast-viewer.com/#code/…. Также может быть полезно взглянуть на использование ts.createFormattingScanner().

Josh Bowden 23.02.2021 19:18
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
8
1
578
1

Ответы 1

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

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