TypeScript编译器API在转换过程中丢失格式



我必须以一种特定的方式修改大约1000个typescript文件:我需要用CallExpression将所有StringLiteralJsxText标记替换为翻译函数,以实现应用程序的国际化。

我已经用Roslyn的C#代码库完成了这样的任务,所以现在我正在尝试用typescript编译器API完成类似的任务。它与Roslyn API非常相似,但它们有一个严重的区别。在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历史和巨大的拉取请求。通过这种方法,我应该在转换后明确使用Prettier,也许我应该将其作为依赖于开发人员的工具安装在我们的CI中,这样我们将来就不会出现这样的问题
  2. 我仍然可以使用AST来检测我的令牌,但我可以在没有ts.Printerts.Transformation的情况下进行转换。我可以在检测阶段处理所有文字,按它们在文件中的位置降序排列,并使用substring或类似的东西替换它们。这是一件相当棘手的事情,我真的不想这么做,但我对第一种选择的缺点不满意

那我该怎么办?我还有其他选择吗?

您可以使用捕获格式和注释的工具,并在转换过程完成时重新生成它们,正如您注意到的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可能是值得的。(如果DMS的TypeScript前端准备好了,那将是一个大灌篮,请执行以上操作(。有时并非如此;YMMV取决于你真正想做什么。DMS最适合用于大型代码库,如果你需要进行复杂的转换,它会非常出色。

我找到了这种方法,它有点破解:

  1. 进行第一次转换,该转换应该检测到两个换行符,如node.getFullText((。以('\n\n'(开头,并在node.addLeadingComment((的帮助下在此节点上方添加一些注释。此注释内容在代码库中应该是唯一的
  2. 执行您的转换
  3. 在原始文件中,在所有事务之后,只需借助正则表达式将注释替换为空字符串即可

最新更新