4 * Copyright 2013 Palantir Technologies, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var path = require("path");
20 var tsutils_1 = require("tsutils");
21 var ts = require("typescript");
22 function getSourceFile(fileName, source) {
23 var normalizedName = path.normalize(fileName).replace(/\\/g, "/");
24 return ts.createSourceFile(normalizedName, source, ts.ScriptTarget.ES5,
25 /*setParentNodes*/ true);
27 exports.getSourceFile = getSourceFile;
28 /** @deprecated See IDisabledInterval. */
29 function doesIntersect(failure,
30 // tslint:disable-next-line:deprecation
32 return disabledIntervals.some(function (interval) {
33 var maxStart = Math.max(interval.startPosition, failure.getStartPosition().getPosition());
34 var minEnd = Math.min(interval.endPosition, failure.getEndPosition().getPosition());
35 return maxStart <= minEnd;
38 exports.doesIntersect = doesIntersect;
40 * @returns true if any modifier kinds passed along exist in the given modifiers array
42 * @deprecated use `hasModifier` from `tsutils`
44 function hasModifier(modifiers) {
45 var modifierKinds = [];
46 for (var _i = 1; _i < arguments.length; _i++) {
47 modifierKinds[_i - 1] = arguments[_i];
49 if (modifiers === undefined || modifierKinds.length === 0) {
52 return modifiers.some(function (m) { return modifierKinds.some(function (k) { return m.kind === k; }); });
54 exports.hasModifier = hasModifier;
56 * Determines if the appropriate bit in the parent (VariableDeclarationList) is set,
57 * which indicates this is a "let" or "const".
59 * @deprecated use `isBlockScopedVariableDeclarationList` from `tsutils`
61 function isBlockScopedVariable(node) {
62 if (node.kind === ts.SyntaxKind.VariableDeclaration) {
63 var parent = node.parent;
64 return (parent.kind === ts.SyntaxKind.CatchClause ||
65 tsutils_1.isBlockScopedVariableDeclarationList(parent));
68 return tsutils_1.isBlockScopedVariableDeclarationList(node.declarationList);
71 exports.isBlockScopedVariable = isBlockScopedVariable;
72 /** @deprecated use `isBlockScopedVariableDeclarationList` and `getDeclarationOfBindingElement` from `tsutils` */
73 function isBlockScopedBindingElement(node) {
74 var variableDeclaration = getBindingElementVariableDeclaration(node); // tslint:disable-line:deprecation
75 // if no variable declaration, it must be a function param, which is block scoped
76 return variableDeclaration === null || isBlockScopedVariable(variableDeclaration); // tslint:disable-line:deprecation
78 exports.isBlockScopedBindingElement = isBlockScopedBindingElement;
79 /** @deprecated use `getDeclarationOfBindingElement` from `tsutils` */
80 function getBindingElementVariableDeclaration(node) {
81 var currentParent = node.parent;
82 while (currentParent.kind !== ts.SyntaxKind.VariableDeclaration) {
83 if (currentParent.parent === undefined) {
84 return null; // function parameter, no variable declaration
87 currentParent = currentParent.parent;
92 exports.getBindingElementVariableDeclaration = getBindingElementVariableDeclaration;
94 * Finds a child of a given node with a given kind.
95 * Note: This uses `node.getChildren()`, which does extra parsing work to include tokens.
97 * @deprecated use `getChildOfKind` from `tsutils`
99 function childOfKind(node, kind) {
100 return node.getChildren().find(function (child) { return child.kind === kind; });
102 exports.childOfKind = childOfKind;
104 * @returns true if some ancestor of `node` satisfies `predicate`, including `node` itself.
106 * @deprecated no longer used, use a `while` loop instead
108 function someAncestor(node, predicate) {
109 return predicate(node) || (node.parent !== undefined && someAncestor(node.parent, predicate)); // tslint:disable-line:deprecation
111 exports.someAncestor = someAncestor;
112 function ancestorWhere(node, predicate) {
115 if (predicate(cur)) {
119 } while (cur !== undefined);
122 exports.ancestorWhere = ancestorWhere;
123 /** @deprecated use `isBinaryExpression(node) && isAssignmentKind(node.operatorToken.kind)` with functions from `tsutils` */
124 function isAssignment(node) {
125 if (node.kind === ts.SyntaxKind.BinaryExpression) {
126 var binaryExpression = node;
127 return (binaryExpression.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
128 binaryExpression.operatorToken.kind <= ts.SyntaxKind.LastAssignment);
134 exports.isAssignment = isAssignment;
136 * Bitwise check for node flags.
138 * @deprecated use `isNodeFlagSet` from `tsutils`
140 function isNodeFlagSet(node, flagToCheck) {
141 // tslint:disable-next-line:no-bitwise
142 return (node.flags & flagToCheck) !== 0;
144 exports.isNodeFlagSet = isNodeFlagSet;
146 * Bitwise check for combined node flags.
148 * @deprecated no longer used
150 function isCombinedNodeFlagSet(node, flagToCheck) {
151 // tslint:disable-next-line:no-bitwise
152 return (ts.getCombinedNodeFlags(node) & flagToCheck) !== 0;
154 exports.isCombinedNodeFlagSet = isCombinedNodeFlagSet;
156 * Bitwise check for combined modifier flags.
158 * @deprecated no longer used
160 function isCombinedModifierFlagSet(node, flagToCheck) {
161 // tslint:disable-next-line:no-bitwise
162 return (ts.getCombinedModifierFlags(node) & flagToCheck) !== 0;
164 exports.isCombinedModifierFlagSet = isCombinedModifierFlagSet;
166 * Bitwise check for type flags.
168 * @deprecated use `isTypeFlagSet` from `tsutils`
170 function isTypeFlagSet(type, flagToCheck) {
171 // tslint:disable-next-line:no-bitwise
172 return (type.flags & flagToCheck) !== 0;
174 exports.isTypeFlagSet = isTypeFlagSet;
176 * Bitwise check for symbol flags.
178 * @deprecated use `isSymbolFlagSet` from `tsutils`
180 function isSymbolFlagSet(symbol, flagToCheck) {
181 // tslint:disable-next-line:no-bitwise
182 return (symbol.flags & flagToCheck) !== 0;
184 exports.isSymbolFlagSet = isSymbolFlagSet;
186 * Bitwise check for object flags.
187 * Does not work with TypeScript 2.0.x
189 * @deprecated use `isObjectFlagSet` from `tsutils`
191 function isObjectFlagSet(objectType, flagToCheck) {
192 // tslint:disable-next-line:no-bitwise
193 return (objectType.objectFlags & flagToCheck) !== 0;
195 exports.isObjectFlagSet = isObjectFlagSet;
197 * @returns true if decl is a nested module declaration, i.e. represents a segment of a dotted module path.
199 * @deprecated use `decl.parent!.kind === ts.SyntaxKind.ModuleDeclaration`
201 function isNestedModuleDeclaration(decl) {
202 // in a declaration expression like 'module a.b.c' - 'a' is the top level module declaration node and 'b' and 'c'
203 // are nested therefore we can depend that a node's position will only match with its name's position for nested
205 return decl.name.pos === decl.pos;
207 exports.isNestedModuleDeclaration = isNestedModuleDeclaration;
208 function unwrapParentheses(node) {
209 while (node.kind === ts.SyntaxKind.ParenthesizedExpression) {
210 node = node.expression;
214 exports.unwrapParentheses = unwrapParentheses;
215 /** @deprecated use `isFunctionScopeBoundary` from `tsutils` */
216 function isScopeBoundary(node) {
217 return (node.kind === ts.SyntaxKind.FunctionDeclaration ||
218 node.kind === ts.SyntaxKind.FunctionExpression ||
219 node.kind === ts.SyntaxKind.PropertyAssignment ||
220 node.kind === ts.SyntaxKind.ShorthandPropertyAssignment ||
221 node.kind === ts.SyntaxKind.MethodDeclaration ||
222 node.kind === ts.SyntaxKind.Constructor ||
223 node.kind === ts.SyntaxKind.ModuleDeclaration ||
224 node.kind === ts.SyntaxKind.ArrowFunction ||
225 node.kind === ts.SyntaxKind.ParenthesizedExpression ||
226 node.kind === ts.SyntaxKind.ClassDeclaration ||
227 node.kind === ts.SyntaxKind.ClassExpression ||
228 node.kind === ts.SyntaxKind.InterfaceDeclaration ||
229 node.kind === ts.SyntaxKind.GetAccessor ||
230 node.kind === ts.SyntaxKind.SetAccessor ||
231 (node.kind === ts.SyntaxKind.SourceFile && ts.isExternalModule(node)));
233 exports.isScopeBoundary = isScopeBoundary;
234 /** @deprecated use `isBlockScopeBoundary` from `tsutils` */
235 function isBlockScopeBoundary(node) {
236 return (isScopeBoundary(node) || // tslint:disable-line:deprecation
237 node.kind === ts.SyntaxKind.Block ||
238 isLoop(node) || // tslint:disable-line:deprecation
239 node.kind === ts.SyntaxKind.WithStatement ||
240 node.kind === ts.SyntaxKind.SwitchStatement ||
241 (node.parent !== undefined &&
242 (node.parent.kind === ts.SyntaxKind.TryStatement ||
243 node.parent.kind === ts.SyntaxKind.IfStatement)));
245 exports.isBlockScopeBoundary = isBlockScopeBoundary;
246 /** @deprecated use `isIterationStatement` from `tsutils` or `typescript` */
247 function isLoop(node) {
248 return (node.kind === ts.SyntaxKind.DoStatement ||
249 node.kind === ts.SyntaxKind.WhileStatement ||
250 node.kind === ts.SyntaxKind.ForStatement ||
251 node.kind === ts.SyntaxKind.ForInStatement ||
252 node.kind === ts.SyntaxKind.ForOfStatement);
254 exports.isLoop = isLoop;
256 * @returns Whether node is a numeric expression.
258 function isNumeric(node) {
259 while (tsutils_1.isPrefixUnaryExpression(node) &&
260 (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken)) {
263 return (node.kind === ts.SyntaxKind.NumericLiteral ||
264 (tsutils_1.isIdentifier(node) && (node.text === "NaN" || node.text === "Infinity")));
266 exports.isNumeric = isNumeric;
268 * Iterate over all tokens of `node`
270 * @description JsDoc comments are treated like regular comments and only visited if `skipTrivia` === false.
272 * @param node The node whose tokens should be visited
273 * @param skipTrivia If set to false all trivia preceeding `node` or any of its children is included
274 * @param cb Is called for every token of `node`. It gets the full text of the SourceFile and the position of the token within that text.
275 * @param filter If provided, will be called for every Node and Token found. If it returns false `cb` will not be called for this subtree.
277 * @deprecated use `forEachToken` or `forEachTokenWithTrivia` from `tsutils`
279 function forEachToken(node, skipTrivia, cb, filter) {
280 // this function will most likely be called with SourceFile anyways, so there is no need for an additional parameter
281 var sourceFile = node.getSourceFile();
282 var fullText = sourceFile.text;
283 var iterateFn = filter === undefined ? iterateChildren : iterateWithFilter;
284 var handleTrivia = skipTrivia ? undefined : createTriviaHandler(sourceFile, cb);
286 // this function is used to save the if condition for the common case where no filter is provided
287 function iterateWithFilter(child) {
289 return iterateChildren(child);
292 function iterateChildren(child) {
293 if (child.kind < ts.SyntaxKind.FirstNode ||
294 // for backwards compatibility to typescript 2.0.10
295 // JsxText was no Token, but a Node in that version
296 child.kind === ts.SyntaxKind.JsxText) {
297 // we found a token, tokens have no children, stop recursing here
298 return callback(child);
300 /* Exclude everything contained in JsDoc, it will be handled with the other trivia anyway.
301 * When we would handle JsDoc tokens like regular ones, we would scan some trivia multiple times.
302 * Even worse, we would scan for trivia inside the JsDoc comment, which yields unexpected results.*/
303 if (child.kind !== ts.SyntaxKind.JSDocComment) {
304 // recurse into Node's children to find tokens
305 return child.getChildren(sourceFile).forEach(iterateFn);
308 function callback(token) {
309 var tokenStart = token.getStart(sourceFile);
310 if (!skipTrivia && tokenStart !== token.pos) {
311 // we only have to handle trivia before each token, because there is nothing after EndOfFileToken
312 handleTrivia(token.pos, tokenStart, token);
314 return cb(fullText, token.kind, { tokenStart: tokenStart, fullStart: token.pos, end: token.end }, token.parent);
317 exports.forEachToken = forEachToken;
318 function createTriviaHandler(sourceFile, cb) {
319 var fullText = sourceFile.text;
320 var scanner = ts.createScanner(sourceFile.languageVersion, false, sourceFile.languageVariant, fullText);
322 * Scan the specified range to get all trivia tokens.
323 * This includes trailing trivia of the last token and the leading trivia of the current token
325 function handleTrivia(start, end, token) {
326 var parent = token.parent;
327 // prevent false positives by not scanning inside JsxText
328 if (!canHaveLeadingTrivia(token.kind, parent)) {
331 scanner.setTextPos(start);
333 // we only get here if start !== end, so we can scan at least one time
335 var kind = scanner.scan();
336 position = scanner.getTextPos();
337 cb(fullText, kind, { tokenStart: scanner.getTokenPos(), end: position, fullStart: start }, parent);
338 } while (position < end);
343 * Iterate over all comments owned by `node` or its children
345 * @deprecated use `forEachComment` from `tsutils`
347 function forEachComment(node, cb) {
348 /* Visit all tokens and skip trivia.
349 Comment ranges between tokens are parsed without the need of a scanner.
350 forEachToken also does intentionally not pay attention to the correct comment ownership of nodes as it always
351 scans all trivia before each token, which could include trailing comments of the previous token.
352 Comment onwership is done right in this function*/
353 // tslint:disable-next-line:deprecation
354 return forEachToken(node, true, function (fullText, tokenKind, pos, parent) {
355 // don't search for comments inside JsxText
356 if (canHaveLeadingTrivia(tokenKind, parent)) {
357 // Comments before the first token (pos.fullStart === 0) are all considered leading comments, so no need for special treatment
358 var comments = ts.getLeadingCommentRanges(fullText, pos.fullStart);
359 if (comments !== undefined) {
360 for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) {
361 var comment = comments_1[_i];
362 cb(fullText, comment.kind, {
364 fullStart: pos.fullStart,
365 tokenStart: comment.pos,
370 if (canHaveTrailingTrivia(tokenKind, parent)) {
371 var comments = ts.getTrailingCommentRanges(fullText, pos.end);
372 if (comments !== undefined) {
373 for (var _a = 0, comments_2 = comments; _a < comments_2.length; _a++) {
374 var comment = comments_2[_a];
375 cb(fullText, comment.kind, {
377 fullStart: pos.fullStart,
378 tokenStart: comment.pos,
385 exports.forEachComment = forEachComment;
386 /** Exclude leading positions that would lead to scanning for trivia inside JsxText */
387 function canHaveLeadingTrivia(tokenKind, parent) {
389 case ts.SyntaxKind.JsxText:
390 return false; // there is no trivia before JsxText
391 case ts.SyntaxKind.OpenBraceToken:
392 // before a JsxExpression inside a JsxElement's body can only be other JsxChild, but no trivia
393 return (parent.kind !== ts.SyntaxKind.JsxExpression ||
394 parent.parent.kind !== ts.SyntaxKind.JsxElement);
395 case ts.SyntaxKind.LessThanToken:
396 switch (parent.kind) {
397 case ts.SyntaxKind.JsxClosingElement:
398 return false; // would be inside the element body
399 case ts.SyntaxKind.JsxOpeningElement:
400 case ts.SyntaxKind.JsxSelfClosingElement:
401 // there can only be leading trivia if we are at the end of the top level element
402 return parent.parent.parent.kind !== ts.SyntaxKind.JsxElement;
410 /** Exclude trailing positions that would lead to scanning for trivia inside JsxText */
411 function canHaveTrailingTrivia(tokenKind, parent) {
413 case ts.SyntaxKind.JsxText:
414 // there is no trivia after JsxText
416 case ts.SyntaxKind.CloseBraceToken:
417 // after a JsxExpression inside a JsxElement's body can only be other JsxChild, but no trivia
418 return (parent.kind !== ts.SyntaxKind.JsxExpression ||
419 parent.parent.kind !== ts.SyntaxKind.JsxElement);
420 case ts.SyntaxKind.GreaterThanToken:
421 switch (parent.kind) {
422 case ts.SyntaxKind.JsxOpeningElement:
423 return false; // would be inside the element
424 case ts.SyntaxKind.JsxClosingElement:
425 case ts.SyntaxKind.JsxSelfClosingElement:
426 // there can only be trailing trivia if we are at the end of the top level element
427 return parent.parent.parent.kind !== ts.SyntaxKind.JsxElement;
436 * Checks if there are any comments between `position` and the next non-trivia token
438 * @param text The text to scan
439 * @param position The position inside `text` where to start scanning. Make sure that this is a valid start position.
440 * This value is typically obtained from `node.getFullStart()` or `node.getEnd()`
442 function hasCommentAfterPosition(text, position) {
443 return (ts.getTrailingCommentRanges(text, position) !== undefined ||
444 ts.getLeadingCommentRanges(text, position) !== undefined);
446 exports.hasCommentAfterPosition = hasCommentAfterPosition;
447 function getEqualsKind(node) {
449 case ts.SyntaxKind.EqualsEqualsToken:
450 return { isPositive: true, isStrict: false };
451 case ts.SyntaxKind.EqualsEqualsEqualsToken:
452 return { isPositive: true, isStrict: true };
453 case ts.SyntaxKind.ExclamationEqualsToken:
454 return { isPositive: false, isStrict: false };
455 case ts.SyntaxKind.ExclamationEqualsEqualsToken:
456 return { isPositive: false, isStrict: true };
461 exports.getEqualsKind = getEqualsKind;
462 function isStrictNullChecksEnabled(options) {
463 return (options.strictNullChecks === true ||
464 (options.strict === true && options.strictNullChecks !== false));
466 exports.isStrictNullChecksEnabled = isStrictNullChecksEnabled;
467 function isNegativeNumberLiteral(node) {
468 return (tsutils_1.isPrefixUnaryExpression(node) &&
469 node.operator === ts.SyntaxKind.MinusToken &&
470 node.operand.kind === ts.SyntaxKind.NumericLiteral);
472 exports.isNegativeNumberLiteral = isNegativeNumberLiteral;
473 /** Wrapper for compatibility with typescript@<2.3.1 */
474 function isWhiteSpace(ch) {
475 // tslint:disable-next-line
476 return (ts.isWhiteSpaceLike || ts.isWhiteSpace)(ch);
478 exports.isWhiteSpace = isWhiteSpace;