【问题标题】:How to serialize a TypeScript AST node & its dependencies如何序列化 TypeScript AST 节点及其依赖项
【发布时间】:2019-12-12 05:14:38
【问题描述】:

如果引用是静态定义的(不是动态创建的),TypeScript 编译器 API 是否提供了一种轻松序列化节点及其依赖项的方法?

例如:

file-a.ts

export const a = () => {
  console.log("a");
}

file-b.ts

import { a } from "./file-a"

const b = () => {
  a()
  console.log("b")
}

当我遇到file-bb 声明时,我想将其提取并打印成这样:

const b = () => {
  (() => {
    console.log("a")
  })()
  console.log("b")
}

...如果进行这种序列化不是编译器 API 的一项功能,是否需要遍历和内联所有声明?还是有更好的方法?

【问题讨论】:

  • 这是在原地修改 TypeScript 文件吗——可以直接编辑文本吗?还是在发出代码之前通过转换 api 完成?另外,你会有类型检查器吗?
  • 是的,我会有一个 TypeChecker。不,不是在原地修改 TypeScript 文件。它用于解决方案构建器。目标是创建一个新的源文件(我们用它做什么并不重要)。新的源文件将仅包含您希望序列化的节点(在本例中为 b),以及内联的任何可静态分析的依赖项...这些可能需要从其他源文件中提取)。

标签: typescript abstract-syntax-tree typescript-compiler-api


【解决方案1】:

没有内置的方法来序列化节点及其所有依赖项,但我认为您不需要在此处进行任何序列化(取决于您的意思)。

要解决这个问题,您可以构建自己的图表,了解所有事物的连接方式,然后遍历该图表以创建最终的单个函数(可以认为是折叠它)。这将帮助您缓存已经完成的工作。不过,您可以不这样做,并在进行时构建语句。

简而言之,可能解释得不够好:

  1. 在该函数 (b) 内,遍历所有节点,找到引用函数外部内容的节点。
  2. 将所有这些节点跟踪到它们的声明(例如,使用类型检查器跟踪调用表达式标识符的符号,直到到达声明)。
  3. 重复直到所有内容都考虑在内。

这里有一个函数,它可能对创建一个节点的深层可变克隆很有用(有点未经测试,可能会有更好的东西......我不确定是否有一种方法可以在不打扰上下文的情况下做到这一点) .您可以使用它来构造节点的副本。

function getDeepMutableClone<T extends ts.Node>(node: T): T {
    return ts.transform(node, [
        context => node => deepCloneWithContext(node, context)
    ]).transformed[0];

    function deepCloneWithContext<T extends ts.Node>(
        node: T,
        context: ts.TransformationContext
    ): T {
        const clonedNode = ts.visitEachChild(
            stripRanges(ts.getMutableClone(node)),
            child => deepCloneWithContext(child, context),
            context
        );
        clonedNode.parent = undefined as any;
        ts.forEachChild(clonedNode, child => { child.parent = clonedNode; });
        return clonedNode;
    }
}

// See https://stackoverflow.com/a/57367717/188246 for
// why this is necessary.
function stripRanges<T extends ts.Node>(node: T) {
    node.pos = -1;
    node.end = -1;
    return node;
}

【讨论】:

  • 使用visitEachChild 的另一种可能可行的方法是检查每个属性以检查它是节点数组还是节点,然后在这种情况下创建一个副本 (example here)。跨度>
猜你喜欢
  • 2011-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2017-11-26
  • 2012-05-17
  • 2019-07-13
  • 1970-01-01
相关资源
最近更新 更多