如何序列化 TypeScript AST 节点及其依赖项

How to serialize a TypeScript AST node & its dependencies

如果引用是静态定义的(不是动态创建的),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,是否需要遍历和内联所有声明?或者有更好的方法吗?

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

为了解决这个问题,您可以建立自己的图表来说明所有事物是如何连接的,然后遍历它以创建最终的单个函数(可以被认为是折叠它)。这将帮助您缓存已经完成的工作。不过,您可以在没有它的情况下做到这一点,并在进行时构建语句。

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

  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  for
// why this is necessary.
function stripRanges<T extends ts.Node>(node: T) {
    node.pos = -1;
    node.end = -1;
    return node;
}