ts-simple-ast
Version:
TypeScript compiler wrapper for static analysis and code manipulation.
1,174 lines • 80.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var errors = require("../../../errors");
var manipulation_1 = require("../../../manipulation");
var typescript_1 = require("../../../typescript");
var utils_1 = require("../../../utils");
var CommentRange_1 = require("./CommentRange");
var Node = /** @class */ (function () {
/**
* Initializes a new instance.
* @private
* @param context - Project context.
* @param node - Underlying node.
* @param sourceFile - Source file for the node.
*/
function Node(context, node, sourceFile) {
/** @internal */
this._wrappedChildCount = 0;
if (context == null || context.compilerFactory == null)
throw new errors.InvalidOperationError("Constructing a node is not supported. Please create a source file from the default export " +
"of the package and manipulate the source file from there.");
this._context = context;
this._compilerNode = node;
this._sourceFile = sourceFile;
}
Object.defineProperty(Node.prototype, "compilerNode", {
/**
* Gets the underlying compiler node.
*/
get: function () {
if (this._compilerNode == null) {
var message = "Attempted to get information from a node that was removed or forgotten.";
if (this._forgottenText != null)
message += "\n\nNode text: " + this._forgottenText;
throw new errors.InvalidOperationError(message);
}
return this._compilerNode;
},
enumerable: true,
configurable: true
});
/**
* Releases the node and all its descendants from the underlying node cache and ast.
*
* This is useful if you want to improve the performance of manipulation by not tracking this node anymore.
*/
Node.prototype.forget = function () {
if (this.wasForgotten())
return;
this.forgetDescendants();
this._forgetOnlyThis();
};
/**
* Forgets the descendants of this node.
*/
Node.prototype.forgetDescendants = function () {
var e_1, _a;
try {
for (var _b = tslib_1.__values(this._getChildrenInCacheIterator()), _c = _b.next(); !_c.done; _c = _b.next()) {
var child = _c.value;
child.forget();
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return this;
};
/**
* Only forgets this node.
* @internal
*/
Node.prototype._forgetOnlyThis = function () {
if (this.wasForgotten())
return;
var parent = this.getParent();
if (parent != null)
parent._wrappedChildCount--;
var parentSyntaxList = this._getParentSyntaxListIfWrapped();
if (parentSyntaxList != null)
parentSyntaxList._wrappedChildCount--;
this._storeTextForForgetting();
this._context.compilerFactory.removeNodeFromCache(this);
this._clearInternals();
};
/**
* Gets if the compiler node was forgotten.
*
* This will be true when the compiler node was forgotten or removed.
*/
Node.prototype.wasForgotten = function () {
return this._compilerNode == null;
};
/**
* Gets if this node has any wrapped children.
* @internal
*/
Node.prototype._hasWrappedChildren = function () {
return this._wrappedChildCount > 0;
};
/**
* @internal
*
* WARNING: This should only be called by the compiler factory!
*/
Node.prototype._replaceCompilerNodeFromFactory = function (compilerNode) {
if (compilerNode == null)
this._storeTextForForgetting();
this._clearInternals();
this._compilerNode = compilerNode;
};
/** @internal */
Node.prototype._storeTextForForgetting = function () {
// check for undefined here just in case
var sourceFileCompilerNode = this._sourceFile && this._sourceFile.compilerNode;
var compilerNode = this._compilerNode;
if (sourceFileCompilerNode == null || compilerNode == null)
return;
this._forgottenText = getText();
function getText() {
var start = compilerNode.getStart(sourceFileCompilerNode);
var length = compilerNode.end - start;
var trimmedLength = Math.min(length, 100);
var text = sourceFileCompilerNode.text.substr(start, trimmedLength);
return trimmedLength !== length ? text + "..." : text;
}
};
/** @internal */
Node.prototype._clearInternals = function () {
this._compilerNode = undefined;
this._childStringRanges = undefined;
clearCommentRanges(this._leadingCommentRanges);
clearCommentRanges(this._trailingCommentRanges);
this._leadingCommentRanges = undefined;
this._trailingCommentRanges = undefined;
function clearCommentRanges(commentRanges) {
if (commentRanges == null)
return;
commentRanges.forEach(function (r) { return r._forget(); });
}
};
/**
* Gets the syntax kind.
*/
Node.prototype.getKind = function () {
return this.compilerNode.kind;
};
/**
* Gets the syntax kind name.
*/
Node.prototype.getKindName = function () {
return utils_1.getSyntaxKindName(this.compilerNode.kind);
};
/**
* Prints the node using the compiler's printer.
* @param options - Options.
*/
Node.prototype.print = function (options) {
if (options === void 0) { options = {}; }
if (options.newLineKind == null)
options.newLineKind = this._context.manipulationSettings.getNewLineKind();
if (this.getKind() === typescript_1.SyntaxKind.SourceFile)
return utils_1.printNode(this.compilerNode, options);
else
return utils_1.printNode(this.compilerNode, this._sourceFile.compilerNode, options);
};
/**
* Gets the symbol or throws an error if it doesn't exist.
*/
Node.prototype.getSymbolOrThrow = function () {
return errors.throwIfNullOrUndefined(this.getSymbol(), "Could not find the node's symbol.");
};
/**
* Gets the compiler symbol or undefined if it doesn't exist.
*/
Node.prototype.getSymbol = function () {
var boundSymbol = this.compilerNode.symbol;
if (boundSymbol != null)
return this._context.compilerFactory.getSymbol(boundSymbol);
var typeChecker = this._context.typeChecker;
var typeCheckerSymbol = typeChecker.getSymbolAtLocation(this);
if (typeCheckerSymbol != null)
return typeCheckerSymbol;
var nameNode = this.compilerNode.name;
if (nameNode != null)
return this._getNodeFromCompilerNode(nameNode).getSymbol();
return undefined;
};
/**
* Gets the type of the node.
*/
Node.prototype.getType = function () {
return this._context.typeChecker.getTypeAtLocation(this);
};
/**
* If the node contains the provided range (inclusive).
* @param pos - Start position.
* @param end - End position.
*/
Node.prototype.containsRange = function (pos, end) {
return this.getPos() <= pos && end <= this.getEnd();
};
/**
* Gets if the specified position is within a string.
* @param pos - Position.
*/
Node.prototype.isInStringAtPos = function (pos) {
var e_2, _a;
errors.throwIfOutOfRange(pos, [this.getPos(), this.getEnd()], "pos");
if (this._childStringRanges == null) {
this._childStringRanges = [];
try {
for (var _b = tslib_1.__values(this._getCompilerDescendantsIterator()), _c = _b.next(); !_c.done; _c = _b.next()) {
var descendant = _c.value;
if (utils_1.isStringKind(descendant.kind))
this._childStringRanges.push([descendant.getStart(this._sourceFile.compilerNode), descendant.getEnd()]);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
}
var InStringRangeComparer = /** @class */ (function () {
function InStringRangeComparer() {
}
InStringRangeComparer.prototype.compareTo = function (value) {
if (pos <= value[0])
return -1;
if (pos >= value[1] - 1)
return 1;
return 0;
};
return InStringRangeComparer;
}());
return utils_1.ArrayUtils.binarySearch(this._childStringRanges, new InStringRangeComparer()) !== -1;
};
Node.prototype.getFirstChildOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getFirstChild(condition), "Could not find a child that matched the specified condition.");
};
Node.prototype.getFirstChild = function (condition) {
var firstChild = this._getCompilerFirstChild(getWrappedCondition(this, condition));
return this._getNodeFromCompilerNodeIfExists(firstChild);
};
Node.prototype.getLastChildOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getLastChild(condition), "Could not find a child that matched the specified condition.");
};
Node.prototype.getLastChild = function (condition) {
var lastChild = this._getCompilerLastChild(getWrappedCondition(this, condition));
return this._getNodeFromCompilerNodeIfExists(lastChild);
};
Node.prototype.getFirstDescendantOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getFirstDescendant(condition), "Could not find a descendant that matched the specified condition.");
};
Node.prototype.getFirstDescendant = function (condition) {
var e_3, _a;
try {
for (var _b = tslib_1.__values(this._getDescendantsIterator()), _c = _b.next(); !_c.done; _c = _b.next()) {
var descendant = _c.value;
if (condition == null || condition(descendant))
return descendant;
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
}
return undefined;
};
Node.prototype.getPreviousSiblingOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getPreviousSibling(condition), "Could not find the previous sibling.");
};
Node.prototype.getPreviousSibling = function (condition) {
var previousSibling = this._getCompilerPreviousSibling(getWrappedCondition(this, condition));
return this._getNodeFromCompilerNodeIfExists(previousSibling);
};
Node.prototype.getNextSiblingOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getNextSibling(condition), "Could not find the next sibling.");
};
Node.prototype.getNextSibling = function (condition) {
var nextSibling = this._getCompilerNextSibling(getWrappedCondition(this, condition));
return this._getNodeFromCompilerNodeIfExists(nextSibling);
};
/**
* Gets the previous siblings.
*
* Note: Closest sibling is the zero index.
*/
Node.prototype.getPreviousSiblings = function () {
var _this = this;
return this._getCompilerPreviousSiblings().map(function (n) { return _this._getNodeFromCompilerNode(n); });
};
/**
* Gets the next siblings.
*
* Note: Closest sibling is the zero index.
*/
Node.prototype.getNextSiblings = function () {
var _this = this;
return this._getCompilerNextSiblings().map(function (n) { return _this._getNodeFromCompilerNode(n); });
};
/**
* Gets all the children of the node.
*/
Node.prototype.getChildren = function () {
var _this = this;
return this._getCompilerChildren().map(function (n) { return _this._getNodeFromCompilerNode(n); });
};
/**
* Gets the child at the specified index.
* @param index - Index of the child.
*/
Node.prototype.getChildAtIndex = function (index) {
return this._getNodeFromCompilerNode(this._getCompilerChildAtIndex(index));
};
/**
* @internal
*/
Node.prototype._getChildrenIterator = function () {
var e_4, _a, _b, _c, compilerChild, e_4_1;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
_d.trys.push([0, 5, 6, 7]);
_b = tslib_1.__values(this._getCompilerChildren()), _c = _b.next();
_d.label = 1;
case 1:
if (!!_c.done) return [3 /*break*/, 4];
compilerChild = _c.value;
return [4 /*yield*/, this._getNodeFromCompilerNode(compilerChild)];
case 2:
_d.sent();
_d.label = 3;
case 3:
_c = _b.next();
return [3 /*break*/, 1];
case 4: return [3 /*break*/, 7];
case 5:
e_4_1 = _d.sent();
e_4 = { error: e_4_1 };
return [3 /*break*/, 7];
case 6:
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_4) throw e_4.error; }
return [7 /*endfinally*/];
case 7: return [2 /*return*/];
}
});
};
/**
* @internal
*/
Node.prototype._getChildrenInCacheIterator = function () {
var e_5, _a, children, children_1, children_1_1, child, e_5_1;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
children = this._getCompilerChildrenFast();
_b.label = 1;
case 1:
_b.trys.push([1, 8, 9, 10]);
children_1 = tslib_1.__values(children), children_1_1 = children_1.next();
_b.label = 2;
case 2:
if (!!children_1_1.done) return [3 /*break*/, 7];
child = children_1_1.value;
if (!this._context.compilerFactory.hasCompilerNode(child)) return [3 /*break*/, 4];
return [4 /*yield*/, this._context.compilerFactory.getExistingCompilerNode(child)];
case 3:
_b.sent();
return [3 /*break*/, 6];
case 4:
if (!(child.kind === typescript_1.SyntaxKind.SyntaxList)) return [3 /*break*/, 6];
// always return syntax lists because their children could be in the cache
return [4 /*yield*/, this._getNodeFromCompilerNode(child)];
case 5:
// always return syntax lists because their children could be in the cache
_b.sent();
_b.label = 6;
case 6:
children_1_1 = children_1.next();
return [3 /*break*/, 2];
case 7: return [3 /*break*/, 10];
case 8:
e_5_1 = _b.sent();
e_5 = { error: e_5_1 };
return [3 /*break*/, 10];
case 9:
try {
if (children_1_1 && !children_1_1.done && (_a = children_1.return)) _a.call(children_1);
}
finally { if (e_5) throw e_5.error; }
return [7 /*endfinally*/];
case 10: return [2 /*return*/];
}
});
};
/**
* Gets the child syntax list or throws if it doesn't exist.
*/
Node.prototype.getChildSyntaxListOrThrow = function () {
return errors.throwIfNullOrUndefined(this.getChildSyntaxList(), "A child syntax list was expected.");
};
/**
* Gets the child syntax list if it exists.
*/
Node.prototype.getChildSyntaxList = function () {
var e_6, _a;
var node = this;
if (utils_1.TypeGuards.isBodyableNode(node) || utils_1.TypeGuards.isBodiedNode(node)) {
do {
var bodyNode = utils_1.TypeGuards.isBodyableNode(node) ? node.getBody() : node.getBody();
if (bodyNode == null)
return undefined;
node = bodyNode;
} while ((utils_1.TypeGuards.isBodyableNode(node) || utils_1.TypeGuards.isBodiedNode(node)) && node.compilerNode.statements == null);
}
if (utils_1.TypeGuards.isSourceFile(node) ||
utils_1.TypeGuards.isBodyableNode(this) ||
utils_1.TypeGuards.isBodiedNode(this) ||
utils_1.TypeGuards.isCaseBlock(this) ||
utils_1.TypeGuards.isCaseClause(this) ||
utils_1.TypeGuards.isDefaultClause(this) ||
utils_1.TypeGuards.isJsxElement(this))
return node.getFirstChildByKind(typescript_1.SyntaxKind.SyntaxList);
var passedBrace = false;
try {
for (var _b = tslib_1.__values(node._getCompilerChildren()), _c = _b.next(); !_c.done; _c = _b.next()) {
var child = _c.value;
if (!passedBrace)
passedBrace = child.kind === typescript_1.SyntaxKind.OpenBraceToken;
else if (child.kind === typescript_1.SyntaxKind.SyntaxList)
return this._getNodeFromCompilerNode(child);
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_6) throw e_6.error; }
}
return undefined;
};
/**
* Invokes the `cbNode` callback for each child and the `cbNodeArray` for every array of nodes stored in properties of the node.
* If `cbNodeArray` is not defined, then it will pass every element of the array to `cbNode`.
*
* @remarks There exists a `traversal.stop()` function on the second parameter that allows stopping iteration.
* @param cbNode - Callback invoked for each child.
* @param cbNodeArray - Callback invoked for each array of nodes.
*/
Node.prototype.forEachChild = function (cbNode, cbNodeArray) {
var _this = this;
var e_7, _a;
var stop = false;
var traversal = {
stop: function () { return stop = true; }
};
var snapshots = [];
// Get all the nodes from the compiler's forEachChild. Taking this snapshot prevents the results of
// .forEachChild from returning out of date nodes due to a manipulation or deletion
this.compilerNode.forEachChild(function (node) {
// use function block to ensure a truthy value is not returned
snapshots.push(_this._getNodeFromCompilerNode(node));
}, cbNodeArray == null ? undefined : function (nodes) {
snapshots.push(nodes.map(function (n) { return _this._getNodeFromCompilerNode(n); }));
});
try {
// now send them to the user
for (var snapshots_1 = tslib_1.__values(snapshots), snapshots_1_1 = snapshots_1.next(); !snapshots_1_1.done; snapshots_1_1 = snapshots_1.next()) {
var snapshot = snapshots_1_1.value;
if (snapshot instanceof Array) {
var filteredNodes = snapshot.filter(function (n) { return !n.wasForgotten(); });
if (filteredNodes.length > 0)
cbNodeArray(filteredNodes, traversal);
}
else if (!snapshot.wasForgotten())
cbNode(snapshot, traversal);
if (stop)
break;
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (snapshots_1_1 && !snapshots_1_1.done && (_a = snapshots_1.return)) _a.call(snapshots_1);
}
finally { if (e_7) throw e_7.error; }
}
};
/**
* Invokes the `cbNode` callback for each descendant and the `cbNodeArray` for every array of nodes stored in properties of the node and descendant nodes.
* If `cbNodeArray` is not defined, then it will pass every element of the array to `cbNode`.
*
* @remarks There exists a `traversal` object on the second parameter that allows various control of iteration.
* @param cbNode - Callback invoked for each descendant.
* @param cbNodeArray - Callback invoked for each array of nodes.
*/
Node.prototype.forEachDescendant = function (cbNode, cbNodeArray) {
var stop = false;
var up = false;
var traversal = {
stop: function () { return stop = true; },
up: function () { return up = true; }
};
var nodeCallback = function (node) {
if (stop)
return;
var skip = false;
cbNode(node, tslib_1.__assign({}, traversal, { skip: function () { return skip = true; } }));
if (stop || skip || up)
return;
if (!node.wasForgotten())
forEachChildForNode(node);
};
var arrayCallback = cbNodeArray == null ? undefined : function (nodes) {
var e_8, _a;
if (stop)
return;
var skip = false;
cbNodeArray(nodes, tslib_1.__assign({}, traversal, { skip: function () { return skip = true; } }));
if (skip)
return;
try {
for (var nodes_1 = tslib_1.__values(nodes), nodes_1_1 = nodes_1.next(); !nodes_1_1.done; nodes_1_1 = nodes_1.next()) {
var node = nodes_1_1.value;
if (stop || up)
return;
forEachChildForNode(node);
}
}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (nodes_1_1 && !nodes_1_1.done && (_a = nodes_1.return)) _a.call(nodes_1);
}
finally { if (e_8) throw e_8.error; }
}
};
forEachChildForNode(this);
function forEachChildForNode(node) {
node.forEachChild(function (innerNode, innerTraversal) {
nodeCallback(innerNode);
if (up) {
innerTraversal.stop();
up = false;
}
}, arrayCallback == null ? undefined : function (nodes, innerTraversal) {
arrayCallback(nodes);
if (up) {
innerTraversal.stop();
up = false;
}
});
}
};
/**
* Gets the node's descendants.
*/
Node.prototype.getDescendants = function () {
return utils_1.ArrayUtils.from(this._getDescendantsIterator());
};
/**
* Gets the node's descendants as an iterator.
* @internal
*/
Node.prototype._getDescendantsIterator = function () {
var e_9, _a, _b, _c, descendant, e_9_1;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
_d.trys.push([0, 5, 6, 7]);
_b = tslib_1.__values(this._getCompilerDescendantsIterator()), _c = _b.next();
_d.label = 1;
case 1:
if (!!_c.done) return [3 /*break*/, 4];
descendant = _c.value;
return [4 /*yield*/, this._getNodeFromCompilerNode(descendant)];
case 2:
_d.sent();
_d.label = 3;
case 3:
_c = _b.next();
return [3 /*break*/, 1];
case 4: return [3 /*break*/, 7];
case 5:
e_9_1 = _d.sent();
e_9 = { error: e_9_1 };
return [3 /*break*/, 7];
case 6:
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_9) throw e_9.error; }
return [7 /*endfinally*/];
case 7: return [2 /*return*/];
}
});
};
/**
* Gets the node's descendant statements.
*/
Node.prototype.getDescendantStatements = function () {
var statements = [];
handleNode(this, this.compilerNode);
return statements;
function handleNode(thisNode, node) {
if (handleStatements(thisNode, node))
return;
else if (node.kind === typescript_1.SyntaxKind.ArrowFunction) {
var arrowFunction = node;
if (arrowFunction.body.kind !== typescript_1.SyntaxKind.Block)
statements.push(thisNode._getNodeFromCompilerNode(arrowFunction.body)); // todo: bug... it's not a statement
else
handleNode(thisNode, arrowFunction.body);
}
else
handleChildren(thisNode, node);
}
function handleStatements(thisNode, node) {
var e_10, _a;
if (node.statements == null)
return false;
try {
for (var _b = tslib_1.__values(node.statements), _c = _b.next(); !_c.done; _c = _b.next()) {
var statement = _c.value;
statements.push(thisNode._getNodeFromCompilerNode(statement));
handleChildren(thisNode, statement);
}
}
catch (e_10_1) { e_10 = { error: e_10_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_10) throw e_10.error; }
}
return true;
}
function handleChildren(thisNode, node) {
typescript_1.ts.forEachChild(node, function (childNode) { return handleNode(thisNode, childNode); });
}
};
/**
* Gets the number of children the node has.
*/
Node.prototype.getChildCount = function () {
return this.compilerNode.getChildCount(this._sourceFile.compilerNode);
};
/**
* Gets the child at the provided text position, or undefined if not found.
* @param pos - Text position to search for.
*/
Node.prototype.getChildAtPos = function (pos) {
var e_11, _a;
if (pos < this.getPos() || pos >= this.getEnd())
return undefined;
try {
for (var _b = tslib_1.__values(this._getCompilerChildren()), _c = _b.next(); !_c.done; _c = _b.next()) {
var child = _c.value;
if (pos >= child.pos && pos < child.end)
return this._getNodeFromCompilerNode(child);
}
}
catch (e_11_1) { e_11 = { error: e_11_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_11) throw e_11.error; }
}
return undefined;
};
/**
* Gets the most specific descendant at the provided text position, or undefined if not found.
* @param pos - Text position to search for.
*/
Node.prototype.getDescendantAtPos = function (pos) {
var node;
while (true) {
var nextNode = (node || this).getChildAtPos(pos);
if (nextNode == null)
return node;
else
node = nextNode;
}
};
/**
* Gets the most specific descendant at the provided start text position with the specified width, or undefined if not found.
* @param start - Start text position to search for.
* @param width - Text length of the node to search for.
*/
Node.prototype.getDescendantAtStartWithWidth = function (start, width) {
var _this = this;
var foundNode;
this._context.compilerFactory.forgetNodesCreatedInBlock(function (remember) {
var nextNode = _this.getSourceFile();
do {
nextNode = nextNode.getChildAtPos(start);
if (nextNode != null) {
if (nextNode.getStart() === start && nextNode.getWidth() === width)
foundNode = nextNode;
else if (foundNode != null)
break; // no need to keep looking
}
} while (nextNode != null);
if (foundNode != null)
remember(foundNode);
});
return foundNode;
};
/**
* Gets the source file text position where the node starts that includes the leading trivia (comments and whitespace).
*/
Node.prototype.getPos = function () {
return this.compilerNode.pos;
};
/**
* Gets the source file text position where the node ends.
*
* @remarks This does not include the following trivia (comments and whitespace).
*/
Node.prototype.getEnd = function () {
return this.compilerNode.end;
};
/**
* Gets the source file text position where the node starts that does not include the leading trivia (comments and whitespace).
* @param includeJsDocComment - Whether to include the JS doc comment.
*/
Node.prototype.getStart = function (includeJsDocComment) {
// rare time a bool parameter will be used... it's because it's done in the ts compiler
return this.compilerNode.getStart(this._sourceFile.compilerNode, includeJsDocComment);
};
/**
* Gets the source file text position of the end of the last significant token or the start of the source file.
*/
Node.prototype.getFullStart = function () {
return this.compilerNode.getFullStart();
};
/**
* Gets the first source file text position that is not whitespace.
*/
Node.prototype.getNonWhitespaceStart = function () {
var parent = this.getParent();
var parentTakesPrecedence = parent != null
&& !utils_1.TypeGuards.isSourceFile(parent)
&& parent.getPos() === this.getPos();
return manipulation_1.getNextNonWhitespacePos(this._sourceFile.getFullText(), parentTakesPrecedence ? this.getStart(true) : this.getPos());
};
/**
* Gets the source file text position going forward the result of .getTrailingTriviaEnd() that is not whitespace.
* @internal
*/
Node.prototype._getTrailingTriviaNonWhitespaceEnd = function () {
return manipulation_1.getPreviousNonWhitespacePos(this._sourceFile.getFullText(), this.getTrailingTriviaEnd());
};
/**
* Gets the text length of the node without trivia.
*/
Node.prototype.getWidth = function () {
return this.compilerNode.getWidth(this._sourceFile.compilerNode);
};
/**
* Gets the text length of the node with trivia.
*/
Node.prototype.getFullWidth = function () {
return this.compilerNode.getFullWidth();
};
/**
* Gets the node's leading trivia's text length.
*/
Node.prototype.getLeadingTriviaWidth = function () {
return this.compilerNode.getLeadingTriviaWidth(this._sourceFile.compilerNode);
};
/**
* Gets the text length from the end of the current node to the next significant token or new line.
*/
Node.prototype.getTrailingTriviaWidth = function () {
return this.getTrailingTriviaEnd() - this.getEnd();
};
/**
* Gets the text position of the next significant token or new line.
*/
Node.prototype.getTrailingTriviaEnd = function () {
var parent = this.getParent();
var end = this.getEnd();
if (parent == null)
return end;
var parentEnd = parent.getEnd();
if (parentEnd === end)
return end;
var trailingComments = this.getTrailingCommentRanges();
var searchStart = getSearchStart();
return manipulation_1.getNextMatchingPos(this._sourceFile.getFullText(), searchStart, function (char) { return char !== " " && char !== "\t"; });
function getSearchStart() {
return trailingComments.length > 0 ? trailingComments[trailingComments.length - 1].getEnd() : end;
}
};
/**
* Gets the text without leading trivia (comments and whitespace).
* @param includeJsDocComment
*/
Node.prototype.getText = function (includeJsDocComment) {
if (includeJsDocComment && utils_1.TypeGuards.isJSDocableNode(this)) {
var docs = this.getJsDocs();
if (docs.length > 0)
return this._sourceFile.getFullText().substring(docs[0].getStart(), this.getEnd());
}
return this.compilerNode.getText(this._sourceFile.compilerNode);
};
/**
* Gets the full text with leading trivia (comments and whitespace).
*/
Node.prototype.getFullText = function () {
return this.compilerNode.getFullText(this._sourceFile.compilerNode);
};
/**
* Gets the combined modifier flags.
*/
Node.prototype.getCombinedModifierFlags = function () {
// todo: make this method only available on declarations in the future.
return typescript_1.ts.getCombinedModifierFlags(this.compilerNode);
};
/**
* Gets the source file.
*/
Node.prototype.getSourceFile = function () {
return this._sourceFile;
};
/**
* Gets a compiler node property wrapped in a Node.
* @param propertyName - Property name.
*/
Node.prototype.getNodeProperty = function (propertyName) {
var _this = this;
var property = this.compilerNode[propertyName];
if (property == null)
return undefined;
else if (property instanceof Array)
return property.map(function (p) { return isNode(p) ? _this._getNodeFromCompilerNode(p) : p; });
else if (isNode(property))
return this._getNodeFromCompilerNode(property);
else
return property;
function isNode(value) {
return typeof value.kind === "number" && typeof value.pos === "number" && typeof value.end === "number";
}
};
Node.prototype.getAncestors = function (includeSyntaxLists) {
if (includeSyntaxLists === void 0) { includeSyntaxLists = false; }
return utils_1.ArrayUtils.from(this._getAncestorsIterator(includeSyntaxLists));
};
/**
* @internal
*/
Node.prototype._getAncestorsIterator = function (includeSyntaxLists) {
function getParent(node) {
return includeSyntaxLists ? node.getParentSyntaxList() || node.getParent() : node.getParent();
}
var parent;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
parent = getParent(this);
_a.label = 1;
case 1:
if (!(parent != null)) return [3 /*break*/, 3];
return [4 /*yield*/, parent];
case 2:
_a.sent();
parent = getParent(parent);
return [3 /*break*/, 1];
case 3: return [2 /*return*/];
}
});
};
/**
* Get the node's parent.
*/
Node.prototype.getParent = function () {
return this._getNodeFromCompilerNodeIfExists(this.compilerNode.parent);
};
/**
* Gets the parent or throws an error if it doesn't exist.
*/
Node.prototype.getParentOrThrow = function () {
return errors.throwIfNullOrUndefined(this.getParent(), "Expected to find a parent.");
};
Node.prototype.getParentWhileOrThrow = function (condition) {
return errors.throwIfNullOrUndefined(this.getParentWhile(condition), "The initial parent did not match the provided condition.");
};
Node.prototype.getParentWhile = function (condition) {
var node = undefined;
var nextParent = this.getParent();
while (nextParent != null && condition(nextParent)) {
node = nextParent;
nextParent = nextParent.getParent();
}
return node;
};
/**
* Goes up the parents (ancestors) of the node while the parent is the specified syntax kind.
* Throws if the initial parent is not the specified syntax kind.
* @param kind - Syntax kind to check for.
*/
Node.prototype.getParentWhileKindOrThrow = function (kind) {
return errors.throwIfNullOrUndefined(this.getParentWhileKind(kind), "The initial parent was not a syntax kind of " + utils_1.getSyntaxKindName(kind) + ".");
};
/**
* Goes up the parents (ancestors) of the node while the parent is the specified syntax kind.
* Returns undefined if the initial parent is not the specified syntax kind.
* @param kind - Syntax kind to check for.
*/
Node.prototype.getParentWhileKind = function (kind) {
return this.getParentWhile(function (n) { return n.getKind() === kind; });
};
/**
* Gets the last token of this node. Usually this is a close brace.
*/
Node.prototype.getLastToken = function () {
var lastToken = this.compilerNode.getLastToken(this._sourceFile.compilerNode);
if (lastToken == null)
throw new errors.NotImplementedError("Not implemented scenario where the last token does not exist.");
return this._getNodeFromCompilerNode(lastToken);
};
/**
* Gets if this node is in a syntax list.
*/
Node.prototype.isInSyntaxList = function () {
return this.getParentSyntaxList() != null;
};
/**
* Gets the parent if it's a syntax list or throws an error otherwise.
*/
Node.prototype.getParentSyntaxListOrThrow = function () {
return errors.throwIfNullOrUndefined(this.getParentSyntaxList(), "Expected the parent to be a syntax list.");
};
/**
* Gets the parent if it's a syntax list.
*/
Node.prototype.getParentSyntaxList = function () {
var syntaxList = utils_1.getParentSyntaxList(this.compilerNode);
return this._getNodeFromCompilerNodeIfExists(syntaxList);
};
/**
* Gets the parent syntax list if it's been wrapped.
* @internal
*/
Node.prototype._getParentSyntaxListIfWrapped = function () {
var parent = this.getParent();
if (parent == null || !parent._hasParsedTokens())
return undefined;
return this.getParentSyntaxList();
};
/**
* Gets the child index of this node relative to the parent.
*/
Node.prototype.getChildIndex = function () {
var e_12, _a;
var parent = this.getParentSyntaxList() || this.getParentOrThrow();
var i = 0;
try {
for (var _b = tslib_1.__values(parent._getCompilerChildren()), _c = _b.next(); !_c.done; _c = _b.next()) {
var child = _c.value;
if (child === this.compilerNode)
return i;
i++;
}
}
catch (e_12_1) { e_12 = { error: e_12_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_12) throw e_12.error; }
}
/* istanbul ignore next */
throw new errors.NotImplementedError("For some reason the child's parent did not contain the child.");
};
/**
* Gets the indentation level of the current node.
*/
Node.prototype.getIndentationLevel = function () {
var indentationText = this._context.manipulationSettings.getIndentationText();
return this._context.languageService.getIdentationAtPosition(this._sourceFile, this.getStart()) / indentationText.length;
};
/**
* Gets the child indentation level of the current node.
*/
Node.prototype.getChildIndentationLevel = function () {
if (utils_1.TypeGuards.isSourceFile(this))
return 0;
return this.getIndentationLevel() + 1;
};
/**
* Gets the indentation text.
* @param offset - Optional number of levels of indentation to add or remove.
*/
Node.prototype.getIndentationText = function (offset) {
if (offset === void 0) { offset = 0; }
return this._getIndentationTextForLevel(this.getIndentationLevel() + offset);
};
/**
* Gets the next indentation level text.
* @param offset - Optional number of levels of indentation to add or remove.
*/
Node.prototype.getChildIndentationText = function (offset) {
if (offset === void 0) { offset = 0; }
return this._getIndentationTextForLevel(this.getChildIndentationLevel() + offset);
};
/**
* @internal
*/
Node.prototype._getIndentationTextForLevel = function (level) {
return utils_1.StringUtils.repeat(this._context.manipulationSettings.getIndentationText(), level);
};
/**
* Gets the position of the start of the line that this node starts on.
* @param includeJsDocComment - Whether to include the JS doc comment or not.
*/
Node.prototype.getStartLinePos = function (includeJsDocComment) {
var sourceFileText = this._sourceFile.getFullText();
return manipulation_1.getPreviousMatchingPos(sourceFileText, this.getStart(includeJsDocComment), function (char) { return char === "\n" || char === "\r"; });
};
/**
* Gets the line number at the start of the node.
* @param includeJsDocComment - Whether to include the JS doc comment or not.
*/
Node.prototype.getStartLineNumber = function (includeJsDocComment) {
return this._sourceFile.getLineNumberAtPos(this.getStartLinePos(includeJsDocComment));
};
/**
* Gets the line number of the end of the node.
*/
Node.prototype.getEndLineNumber = function () {
var sourceFileText = this._sourceFile.getFullText();
var endLinePos = manipulation_1.getPreviousMatchingPos(sourceFileText, this.getEnd(), function (char) { return char === "\n" || char === "\r"; });
return this._sourceFile.getLineNumberAtPos(endLinePos);
};
/**
* Gets if this is the first node on the current line.
*/
Node.prototype.isFirstNodeOnLine = function () {
var sourceFileText = this._sourceFile.getFullText();
var startPos = this.getNonWhitespaceStart();
for (var i = startPos - 1; i >= 0; i--) {
var currentChar = sourceFileText[i];
if (currentChar === " " || currentChar === "\t")
continue;
if (currentChar === "\n")
return true;
return false;
}
return true; // first node on the first line
};
Node.prototype.replaceWithText = function (textOrWriterFunction, writer) {
var newText = utils_1.getTextFromStringOrWriter(writer || this._getWriterWithQueuedIndentation(), textOrWriterFunction);
if (utils_1.TypeGuards.isSourceFile(this)) {
this.replaceText([this.getPos(), this.getEnd()], newText);
return this;
}
var parent = this.getParentSyntaxList() || this.getParentOrThrow();
var childIndex = this.getChildIndex();
var start = this.getStart(true);
manipulation_1.insertIntoParentTextRange({
parent: parent,
insertPos: start,
newText: newText,
replacing: {
textLength: this.getEnd() - start
}
});
return parent.getChildren()[childIndex];
};
/**
* Prepends the specified whitespace to current node.
* @param textOrWriterFunction - Text or writer function.
*/
Node.prototype.prependWhitespace = function (textOrWriterFunction) {
insertWhiteSpaceTextAtPos(this, this.getStart(true), textOrWriterFunction, "prependWhitespace");
};
/**
* Appends the specified whitespace to current node.
* @param textOrWriterFunction - Text or writer function.
*/
Node.prototype.appendWhitespace = function (textOrWriterFunction) {
insertWhiteSpaceTextAtPos(this, this.getEnd(), textOrWriterFunction, "appendWhitespace");
};
/**
* Formats the node's text using the internal TypeScript formatting API.
* @param settings - Format code settings.
*/
Node.prototype.formatText = function (settings) {
if (settings === void 0) { settings = {}; }
var formattingEdits = this._context.languageService.getFormattingEditsForRange(this._sourceFile.getFilePath(), [this.getStart(true), this.getEnd()], settings);
manipulation_1.replaceSourceFileTextForFormatting({
sourceFile: this._sourceFile,
newText: manipulation_1.getTextFromFormattingEdits(this._sourceFile, formattingEdits)
});
};
/**
* Transforms the node using the compiler api nodes and functions (experimental).
*
* WARNING: This will forget descendants of transformed nodes.
* @example Increments all the numeric literals in a source file.
* ```ts
* sourceFile.transform(traversal => {
* const node = traversal.visitChildren(); // recommend always visiting the children first (post order)
* if (ts.isNumericLiteral(node))
* return ts.createNumericLiteral((parseInt(node.text, 10) + 1).toString());
* return node;
* });
* ```
* @example Updates the class declaration node without visiting the children.
* ```ts
* const classDec = sourceFile.getClassOrThrow("MyClass");
* classDec.transform(traversal => {
* const node = traversal.currentNode;
* return ts.updateClassDeclaration(node, undefined, undefined, ts.createIdentifier("MyUpdatedClass"), undefined, undefined, []);
* });
* ```
*/
Node.prototype.transform = function (visitNode) {
var compilerFactory = this._context.compilerFactory;
var printer = typescript_1.ts.createPrinter({
newLine: this._context.manipulationSettings.getNewLineKind(),
removeComments: false
});
var transformations = [];
var compilerSourceFile = this._sourceFile.compilerNode;
var compilerNode = this.compilerNode;
var transformerFactory = function (context) {
return function (rootNode) { return innerVisit(rootNode, context); };
};
typescript_1.ts.transform(compilerNode, [transformerFactory], this._context.compilerOptions.get());
manipulation_1.replaceSourceFileTextStraight({
sourceFile: this._sourceFile,
newText: getTransformedText()
});
return this;
function innerVisit(node, context) {
var traversal = {
visitChildren: function () {
node = typescript_1.ts.visitEachChild(node, function (child) { return innerVisit(child, context); }, context);
return node;
},
currentNode: node
};
var resultNode = visitNode(traversal);
handleTransformation(node, resultNode);
return resultNode;
}
function handleTransformation(oldNode, newNode) {
if (oldNode === newNode)
return;
var start = oldNode.getStart(compilerSourceFile, true);
var end = oldNode.end;
var lastTransformation = transformations[transformations.length - 1];
// remove the last transformation if it's nested within this transformation
if (lastTransformation != null && lastTransformation.start > start)