UNPKG

ts-simple-ast

Version:

TypeScript compiler wrapper for AST navigation and code generation.

118 lines (116 loc) 5.27 kB
"use strict"; 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