There's not a built in way to serialize a node and all its dependencies, but I don't believe you need to do any serialization here (depending on what you mean by that).
To solve this, you could build up your own graph of how everything is connected and then traverse that to create a final single function (could be thought of as collapsing it). That would help you cache work that's already done. You could do it without that though and just construct the statements as you go along.
In short and probably not explaining it well enough:
- Within that function (
b
), traverse all the nodes finding nodes that reference something outside the function.
- Follow all those nodes to their declarations (ex. use the type checker to follow the symbols of a call expression's identifier until you reach the declaration).
- Repeat until everything is accounted for.
Here's a function that might be useful for making a deep mutable clone of a node (kind of untested and there might be something better... I'm not sure if there's a way to do this without bothering with a context). You could use this to construct copies of nodes.
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;
}