diff options
Diffstat (limited to 'node_modules/tslint/lib/language/utils.js')
-rw-r--r-- | node_modules/tslint/lib/language/utils.js | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/node_modules/tslint/lib/language/utils.js b/node_modules/tslint/lib/language/utils.js new file mode 100644 index 000000000..4f1fa52cf --- /dev/null +++ b/node_modules/tslint/lib/language/utils.js @@ -0,0 +1,402 @@ +"use strict"; +/** + * @license + * Copyright 2013 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +var path = require("path"); +var tsutils_1 = require("tsutils"); +var ts = require("typescript"); +function getSourceFile(fileName, source) { + var normalizedName = path.normalize(fileName).replace(/\\/g, "/"); + return ts.createSourceFile(normalizedName, source, ts.ScriptTarget.ES5, /*setParentNodes*/ true); +} +exports.getSourceFile = getSourceFile; +/** @deprecated See IDisabledInterval. */ +function doesIntersect(failure, disabledIntervals) { + return disabledIntervals.some(function (interval) { + var maxStart = Math.max(interval.startPosition, failure.getStartPosition().getPosition()); + var minEnd = Math.min(interval.endPosition, failure.getEndPosition().getPosition()); + return maxStart <= minEnd; + }); +} +exports.doesIntersect = doesIntersect; +/** + * @returns true if any modifier kinds passed along exist in the given modifiers array + */ +function hasModifier(modifiers) { + var modifierKinds = []; + for (var _i = 1; _i < arguments.length; _i++) { + modifierKinds[_i - 1] = arguments[_i]; + } + if (modifiers === undefined || modifierKinds.length === 0) { + return false; + } + return modifiers.some(function (m) { + return modifierKinds.some(function (k) { return m.kind === k; }); + }); +} +exports.hasModifier = hasModifier; +/** + * Determines if the appropriate bit in the parent (VariableDeclarationList) is set, + * which indicates this is a "let" or "const". + */ +function isBlockScopedVariable(node) { + if (node.kind === ts.SyntaxKind.VariableDeclaration) { + var parent = node.parent; + return parent.kind === ts.SyntaxKind.CatchClause || tsutils_1.isBlockScopedVariableDeclarationList(parent); + } + else { + return tsutils_1.isBlockScopedVariableDeclarationList(node.declarationList); + } +} +exports.isBlockScopedVariable = isBlockScopedVariable; +function isBlockScopedBindingElement(node) { + var variableDeclaration = getBindingElementVariableDeclaration(node); + // if no variable declaration, it must be a function param, which is block scoped + return (variableDeclaration == null) || isBlockScopedVariable(variableDeclaration); +} +exports.isBlockScopedBindingElement = isBlockScopedBindingElement; +function getBindingElementVariableDeclaration(node) { + var currentParent = node.parent; + while (currentParent.kind !== ts.SyntaxKind.VariableDeclaration) { + if (currentParent.parent == null) { + return null; // function parameter, no variable declaration + } + else { + currentParent = currentParent.parent; + } + } + return currentParent; +} +exports.getBindingElementVariableDeclaration = getBindingElementVariableDeclaration; +/** + * Finds a child of a given node with a given kind. + * Note: This uses `node.getChildren()`, which does extra parsing work to include tokens. + */ +function childOfKind(node, kind) { + return node.getChildren().find(function (child) { return child.kind === kind; }); +} +exports.childOfKind = childOfKind; +/** + * @returns true if some ancestor of `node` satisfies `predicate`, including `node` itself. + */ +function someAncestor(node, predicate) { + return predicate(node) || (node.parent != null && someAncestor(node.parent, predicate)); +} +exports.someAncestor = someAncestor; +function ancestorWhere(node, predicate) { + var cur = node; + do { + if (predicate(cur)) { + return cur; + } + cur = cur.parent; + } while (cur !== undefined); + return undefined; +} +exports.ancestorWhere = ancestorWhere; +function isAssignment(node) { + if (node.kind === ts.SyntaxKind.BinaryExpression) { + var binaryExpression = node; + return binaryExpression.operatorToken.kind >= ts.SyntaxKind.FirstAssignment + && binaryExpression.operatorToken.kind <= ts.SyntaxKind.LastAssignment; + } + else { + return false; + } +} +exports.isAssignment = isAssignment; +/** + * Bitwise check for node flags. + */ +function isNodeFlagSet(node, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (node.flags & flagToCheck) !== 0; +} +exports.isNodeFlagSet = isNodeFlagSet; +/** + * Bitwise check for combined node flags. + */ +function isCombinedNodeFlagSet(node, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (ts.getCombinedNodeFlags(node) & flagToCheck) !== 0; +} +exports.isCombinedNodeFlagSet = isCombinedNodeFlagSet; +/** + * Bitwise check for combined modifier flags. + */ +function isCombinedModifierFlagSet(node, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (ts.getCombinedModifierFlags(node) & flagToCheck) !== 0; +} +exports.isCombinedModifierFlagSet = isCombinedModifierFlagSet; +/** + * Bitwise check for type flags. + */ +function isTypeFlagSet(type, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (type.flags & flagToCheck) !== 0; +} +exports.isTypeFlagSet = isTypeFlagSet; +/** + * Bitwise check for symbol flags. + */ +function isSymbolFlagSet(symbol, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (symbol.flags & flagToCheck) !== 0; +} +exports.isSymbolFlagSet = isSymbolFlagSet; +/** + * Bitwise check for object flags. + * Does not work with TypeScript 2.0.x + */ +function isObjectFlagSet(objectType, flagToCheck) { + // tslint:disable-next-line:no-bitwise + return (objectType.objectFlags & flagToCheck) !== 0; +} +exports.isObjectFlagSet = isObjectFlagSet; +/** + * @returns true if decl is a nested module declaration, i.e. represents a segment of a dotted module path. + */ +function isNestedModuleDeclaration(decl) { + // in a declaration expression like 'module a.b.c' - 'a' is the top level module declaration node and 'b' and 'c' + // are nested therefore we can depend that a node's position will only match with its name's position for nested + // nodes + return decl.name.pos === decl.pos; +} +exports.isNestedModuleDeclaration = isNestedModuleDeclaration; +function unwrapParentheses(node) { + while (node.kind === ts.SyntaxKind.ParenthesizedExpression) { + node = node.expression; + } + return node; +} +exports.unwrapParentheses = unwrapParentheses; +function isScopeBoundary(node) { + return node.kind === ts.SyntaxKind.FunctionDeclaration + || node.kind === ts.SyntaxKind.FunctionExpression + || node.kind === ts.SyntaxKind.PropertyAssignment + || node.kind === ts.SyntaxKind.ShorthandPropertyAssignment + || node.kind === ts.SyntaxKind.MethodDeclaration + || node.kind === ts.SyntaxKind.Constructor + || node.kind === ts.SyntaxKind.ModuleDeclaration + || node.kind === ts.SyntaxKind.ArrowFunction + || node.kind === ts.SyntaxKind.ParenthesizedExpression + || node.kind === ts.SyntaxKind.ClassDeclaration + || node.kind === ts.SyntaxKind.ClassExpression + || node.kind === ts.SyntaxKind.InterfaceDeclaration + || node.kind === ts.SyntaxKind.GetAccessor + || node.kind === ts.SyntaxKind.SetAccessor + || node.kind === ts.SyntaxKind.SourceFile && ts.isExternalModule(node); +} +exports.isScopeBoundary = isScopeBoundary; +function isBlockScopeBoundary(node) { + return isScopeBoundary(node) + || node.kind === ts.SyntaxKind.Block + || isLoop(node) + || node.kind === ts.SyntaxKind.WithStatement + || node.kind === ts.SyntaxKind.SwitchStatement + || node.parent !== undefined + && (node.parent.kind === ts.SyntaxKind.TryStatement + || node.parent.kind === ts.SyntaxKind.IfStatement); +} +exports.isBlockScopeBoundary = isBlockScopeBoundary; +function isLoop(node) { + return node.kind === ts.SyntaxKind.DoStatement + || node.kind === ts.SyntaxKind.WhileStatement + || node.kind === ts.SyntaxKind.ForStatement + || node.kind === ts.SyntaxKind.ForInStatement + || node.kind === ts.SyntaxKind.ForOfStatement; +} +exports.isLoop = isLoop; +/** + * Iterate over all tokens of `node` + * + * @description JsDoc comments are treated like regular comments and only visited if `skipTrivia` === false. + * + * @param node The node whose tokens should be visited + * @param skipTrivia If set to false all trivia preceeding `node` or any of its children is included + * @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. + * @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. + */ +function forEachToken(node, skipTrivia, cb, filter) { + // this function will most likely be called with SourceFile anyways, so there is no need for an additional parameter + var sourceFile = node.getSourceFile(); + var fullText = sourceFile.text; + var iterateFn = filter === undefined ? iterateChildren : iterateWithFilter; + var handleTrivia = skipTrivia ? undefined : createTriviaHandler(sourceFile, cb); + iterateFn(node); + // this function is used to save the if condition for the common case where no filter is provided + function iterateWithFilter(child) { + if (filter(child)) { + return iterateChildren(child); + } + } + function iterateChildren(child) { + if (child.kind < ts.SyntaxKind.FirstNode || + // for backwards compatibility to typescript 2.0.10 + // JsxText was no Token, but a Node in that version + child.kind === ts.SyntaxKind.JsxText) { + // we found a token, tokens have no children, stop recursing here + return callback(child); + } + /* Exclude everything contained in JsDoc, it will be handled with the other trivia anyway. + * When we would handle JsDoc tokens like regular ones, we would scan some trivia multiple times. + * Even worse, we would scan for trivia inside the JsDoc comment, which yields unexpected results.*/ + if (child.kind !== ts.SyntaxKind.JSDocComment) { + // recurse into Node's children to find tokens + return child.getChildren(sourceFile).forEach(iterateFn); + } + } + function callback(token) { + var tokenStart = token.getStart(sourceFile); + if (!skipTrivia && tokenStart !== token.pos) { + // we only have to handle trivia before each token, because there is nothing after EndOfFileToken + handleTrivia(token.pos, tokenStart, token); + } + return cb(fullText, token.kind, { tokenStart: tokenStart, fullStart: token.pos, end: token.end }, token.parent); + } +} +exports.forEachToken = forEachToken; +function createTriviaHandler(sourceFile, cb) { + var fullText = sourceFile.text; + var scanner = ts.createScanner(sourceFile.languageVersion, false, sourceFile.languageVariant, fullText); + /** + * Scan the specified range to get all trivia tokens. + * This includes trailing trivia of the last token and the leading trivia of the current token + */ + function handleTrivia(start, end, token) { + var parent = token.parent; + // prevent false positives by not scanning inside JsxText + if (!canHaveLeadingTrivia(token.kind, parent)) { + return; + } + scanner.setTextPos(start); + var position; + // we only get here if start !== end, so we can scan at least one time + do { + var kind = scanner.scan(); + position = scanner.getTextPos(); + cb(fullText, kind, { tokenStart: scanner.getTokenPos(), end: position, fullStart: start }, parent); + } while (position < end); + } + return handleTrivia; +} +/** Iterate over all comments owned by `node` or its children */ +function forEachComment(node, cb) { + /* Visit all tokens and skip trivia. + Comment ranges between tokens are parsed without the need of a scanner. + forEachToken also does intentionally not pay attention to the correct comment ownership of nodes as it always + scans all trivia before each token, which could include trailing comments of the previous token. + Comment onwership is done right in this function*/ + return forEachToken(node, true, function (fullText, tokenKind, pos, parent) { + // don't search for comments inside JsxText + if (canHaveLeadingTrivia(tokenKind, parent)) { + // Comments before the first token (pos.fullStart === 0) are all considered leading comments, so no need for special treatment + var comments = ts.getLeadingCommentRanges(fullText, pos.fullStart); + if (comments !== undefined) { + for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) { + var comment = comments_1[_i]; + cb(fullText, comment.kind, { fullStart: pos.fullStart, tokenStart: comment.pos, end: comment.end }); + } + } + } + if (canHaveTrailingTrivia(tokenKind, parent)) { + var comments = ts.getTrailingCommentRanges(fullText, pos.end); + if (comments !== undefined) { + for (var _a = 0, comments_2 = comments; _a < comments_2.length; _a++) { + var comment = comments_2[_a]; + cb(fullText, comment.kind, { fullStart: pos.fullStart, tokenStart: comment.pos, end: comment.end }); + } + } + } + }); +} +exports.forEachComment = forEachComment; +/** Exclude leading positions that would lead to scanning for trivia inside JsxText */ +function canHaveLeadingTrivia(tokenKind, parent) { + switch (tokenKind) { + case ts.SyntaxKind.JsxText: + return false; // there is no trivia before JsxText + case ts.SyntaxKind.OpenBraceToken: + // before a JsxExpression inside a JsxElement's body can only be other JsxChild, but no trivia + return parent.kind !== ts.SyntaxKind.JsxExpression || parent.parent.kind !== ts.SyntaxKind.JsxElement; + case ts.SyntaxKind.LessThanToken: + switch (parent.kind) { + case ts.SyntaxKind.JsxClosingElement: + return false; // would be inside the element body + case ts.SyntaxKind.JsxOpeningElement: + case ts.SyntaxKind.JsxSelfClosingElement: + // there can only be leading trivia if we are at the end of the top level element + return parent.parent.parent.kind !== ts.SyntaxKind.JsxElement; + default: + return true; + } + default: + return true; + } +} +/** Exclude trailing positions that would lead to scanning for trivia inside JsxText */ +function canHaveTrailingTrivia(tokenKind, parent) { + switch (tokenKind) { + case ts.SyntaxKind.JsxText: + // there is no trivia after JsxText + return false; + case ts.SyntaxKind.CloseBraceToken: + // after a JsxExpression inside a JsxElement's body can only be other JsxChild, but no trivia + return parent.kind !== ts.SyntaxKind.JsxExpression || parent.parent.kind !== ts.SyntaxKind.JsxElement; + case ts.SyntaxKind.GreaterThanToken: + switch (parent.kind) { + case ts.SyntaxKind.JsxOpeningElement: + return false; // would be inside the element + case ts.SyntaxKind.JsxClosingElement: + case ts.SyntaxKind.JsxSelfClosingElement: + // there can only be trailing trivia if we are at the end of the top level element + return parent.parent.parent.kind !== ts.SyntaxKind.JsxElement; + default: + return true; + } + default: + return true; + } +} +/** + * Checks if there are any comments between `position` and the next non-trivia token + * + * @param text The text to scan + * @param position The position inside `text` where to start scanning. Make sure that this is a valid start position. + * This value is typically obtained from `node.getFullStart()` or `node.getEnd()` + */ +function hasCommentAfterPosition(text, position) { + return ts.getTrailingCommentRanges(text, position) !== undefined || + ts.getLeadingCommentRanges(text, position) !== undefined; +} +exports.hasCommentAfterPosition = hasCommentAfterPosition; +function getEqualsKind(node) { + switch (node.kind) { + case ts.SyntaxKind.EqualsEqualsToken: + return { isPositive: true, isStrict: false }; + case ts.SyntaxKind.EqualsEqualsEqualsToken: + return { isPositive: true, isStrict: true }; + case ts.SyntaxKind.ExclamationEqualsToken: + return { isPositive: false, isStrict: false }; + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + return { isPositive: false, isStrict: true }; + default: + return undefined; + } +} +exports.getEqualsKind = getEqualsKind; |