ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
118 lines (116 loc) • 5.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const ts = require("typescript");
const errors = require("./../../errors");
const utils_1 = require("./../../utils");
const getInsertErrorMessageText_1 = require("./../getInsertErrorMessageText");
const areNodesEqual_1 = require("./areNodesEqual");
/**
* Replaces with tree that creates a syntax list.
*/
function replaceTreeCreatingSyntaxList(opts) {
const { parent, replacementSourceFile } = opts;
replaceTree({
parent,
childCount: 1,
replacementSourceFile,
isFirstChild: (currentNode, newNode) => newNode.getKind() === ts.SyntaxKind.SyntaxList && currentNode.getKind() !== ts.SyntaxKind.SyntaxList
});
}
exports.replaceTreeCreatingSyntaxList = replaceTreeCreatingSyntaxList;
/**
* Replaces with tree based on the child index from the parent.
*/
function replaceTreeWithChildIndex(opts) {
const { replacementSourceFile, parent, childIndex, childCount, replacingNodes } = opts;
const parentChildren = parent.getChildren();
errors.throwIfOutOfRange(childIndex, [0, parentChildren.length], "opts.childIndex");
if (childCount < 0)
errors.throwIfOutOfRange(childCount, [childIndex - parentChildren.length, 0], "opts.childCount");
let i = 0;
const isFirstChild = () => i++ === childIndex;
replaceTree({
replacementSourceFile,
parent,
isFirstChild,
childCount,
replacingNodes
});
}
exports.replaceTreeWithChildIndex = replaceTreeWithChildIndex;
/**
* Replaces the tree with a new one.
*/
function replaceTree(opts) {
const { replacementSourceFile, parent: changingParent, isFirstChild, childCount } = opts;
const sourceFile = changingParent.getSourceFile();
const compilerFactory = sourceFile.global.compilerFactory;
const replacingNodes = opts.replacingNodes == null ? undefined : [...opts.replacingNodes];
const changingParentParent = changingParent.getParentSyntaxList() || changingParent.getParent();
if (changingParent === sourceFile)
handleChangingParent(sourceFile, replacementSourceFile);
else
handleNode(sourceFile, replacementSourceFile);
function handleNode(currentNode, newNode) {
/* istanbul ignore if */
if (currentNode.getKind() !== newNode.getKind())
throw new errors.InvalidOperationError(getInsertErrorMessageText_1.getInsertErrorMessageText("Error replacing tree!", currentNode, newNode));
const newNodeChildren = newNode.getChildrenIterator();
const areParentParentsEqual = areNodesEqual_1.areNodesEqual(newNode, changingParentParent);
for (const currentNodeChild of currentNode.getChildrenIterator()) {
const newNodeChild = newNodeChildren.next().value;
if (areParentParentsEqual && areNodesEqual_1.areNodesEqual(newNodeChild, changingParent))
handleChangingParent(currentNodeChild, newNodeChild);
else
handleNode(currentNodeChild, newNodeChild);
}
/* istanbul ignore if */
if (!newNodeChildren.next().done)
throw new Error("Error replacing tree: Should not have new children left over."); // todo: better error message
compilerFactory.replaceCompilerNode(currentNode, newNode.compilerNode);
}
function handleChangingParent(currentNode, newNode) {
const currentNodeChildren = new utils_1.AdvancedIterator(currentNode.getChildrenIterator());
const newNodeChildren = new utils_1.AdvancedIterator(newNode.getChildrenIterator());
let count = childCount;
// get the first child
while (!currentNodeChildren.done && !newNodeChildren.done && !isFirstChild(currentNodeChildren.peek, newNodeChildren.peek))
handleNode(currentNodeChildren.next(), newNodeChildren.next());
// try replacing any nodes
while (!currentNodeChildren.done && tryReplaceNode(currentNodeChildren.peek))
currentNodeChildren.next();
// add or remove the items
if (count > 0) {
while (count > 0) {
newNodeChildren.next().setSourceFile(sourceFile);
count--;
}
}
else if (count < 0) {
while (count < 0) {
currentNodeChildren.next().dispose();
count++;
}
}
// handle the rest
while (!currentNodeChildren.done) {
handleNode(currentNodeChildren.next(), newNodeChildren.next());
}
// ensure the new children iterator is done too
if (!newNodeChildren.done)
throw new Error("Error replacing tree: Should not have more children left over."); // todo: better error message
compilerFactory.replaceCompilerNode(currentNode, newNode.compilerNode);
}
function tryReplaceNode(currentNode) {
if (replacingNodes == null || replacingNodes.length === 0)
return false;
const index = replacingNodes.indexOf(currentNode);
if (index === -1)
return false;
replacingNodes.splice(index, 1);
currentNode.dispose();
return true;
}
}
exports.replaceTree = replaceTree;
//# sourceMappingURL=replaceTree.js.map