securityos/node_modules/tsutils/util/util.js

1686 lines
77 KiB
JavaScript
Raw Normal View History

2024-09-06 15:32:35 +00:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isValidIdentifier = exports.getLineBreakStyle = exports.getLineRanges = exports.forEachComment = exports.forEachTokenWithTrivia = exports.forEachToken = exports.isFunctionWithBody = exports.hasOwnThisReference = exports.isBlockScopeBoundary = exports.isFunctionScopeBoundary = exports.isTypeScopeBoundary = exports.isScopeBoundary = exports.ScopeBoundarySelector = exports.ScopeBoundary = exports.isInSingleStatementContext = exports.isBlockScopedDeclarationStatement = exports.isBlockScopedVariableDeclaration = exports.isBlockScopedVariableDeclarationList = exports.getVariableDeclarationKind = exports.VariableDeclarationKind = exports.forEachDeclaredVariable = exports.forEachDestructuringIdentifier = exports.getPropertyName = exports.getWrappedNodeAtPosition = exports.getAstNodeAtPosition = exports.commentText = exports.isPositionInComment = exports.getCommentAtPosition = exports.getTokenAtPosition = exports.getNextToken = exports.getPreviousToken = exports.getNextStatement = exports.getPreviousStatement = exports.isModifierFlagSet = exports.isObjectFlagSet = exports.isSymbolFlagSet = exports.isTypeFlagSet = exports.isNodeFlagSet = exports.hasAccessModifier = exports.isParameterProperty = exports.hasModifier = exports.getModifier = exports.isThisParameter = exports.isKeywordKind = exports.isJsDocKind = exports.isTypeNodeKind = exports.isAssignmentKind = exports.isNodeKind = exports.isTokenKind = exports.getChildOfKind = void 0;
exports.getBaseOfClassLikeExpression = exports.hasExhaustiveCaseClauses = exports.formatPseudoBigInt = exports.unwrapParentheses = exports.getSingleLateBoundPropertyNameOfPropertyName = exports.getLateBoundPropertyNamesOfPropertyName = exports.getLateBoundPropertyNames = exports.getPropertyNameOfWellKnownSymbol = exports.isWellKnownSymbolLiterally = exports.isBindableObjectDefinePropertyCall = exports.isReadonlyAssignmentDeclaration = exports.isInConstContext = exports.isConstAssertion = exports.getTsCheckDirective = exports.getCheckJsDirective = exports.isAmbientModule = exports.isCompilerOptionEnabled = exports.isStrictCompilerOptionEnabled = exports.getIIFE = exports.isAmbientModuleBlock = exports.isStatementInAmbientContext = exports.findImportLikeNodes = exports.findImports = exports.ImportKind = exports.parseJsDocOfNode = exports.getJsDoc = exports.canHaveJsDoc = exports.isReassignmentTarget = exports.getAccessKind = exports.AccessKind = exports.isExpressionValueUsed = exports.getDeclarationOfBindingElement = exports.hasSideEffects = exports.SideEffectOptions = exports.isSameLine = exports.isNumericPropertyName = exports.isValidJsxIdentifier = exports.isValidNumericLiteral = exports.isValidPropertyName = exports.isValidPropertyAccess = void 0;
const ts = require("typescript");
const node_1 = require("../typeguard/node");
const _3_2_1 = require("../typeguard/3.2");
const type_1 = require("./type");
function getChildOfKind(node, kind, sourceFile) {
for (const child of node.getChildren(sourceFile))
if (child.kind === kind)
return child;
}
exports.getChildOfKind = getChildOfKind;
function isTokenKind(kind) {
return kind >= ts.SyntaxKind.FirstToken && kind <= ts.SyntaxKind.LastToken;
}
exports.isTokenKind = isTokenKind;
function isNodeKind(kind) {
return kind >= ts.SyntaxKind.FirstNode;
}
exports.isNodeKind = isNodeKind;
function isAssignmentKind(kind) {
return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;
}
exports.isAssignmentKind = isAssignmentKind;
function isTypeNodeKind(kind) {
return kind >= ts.SyntaxKind.FirstTypeNode && kind <= ts.SyntaxKind.LastTypeNode;
}
exports.isTypeNodeKind = isTypeNodeKind;
function isJsDocKind(kind) {
return kind >= ts.SyntaxKind.FirstJSDocNode && kind <= ts.SyntaxKind.LastJSDocNode;
}
exports.isJsDocKind = isJsDocKind;
function isKeywordKind(kind) {
return kind >= ts.SyntaxKind.FirstKeyword && kind <= ts.SyntaxKind.LastKeyword;
}
exports.isKeywordKind = isKeywordKind;
function isThisParameter(parameter) {
return parameter.name.kind === ts.SyntaxKind.Identifier && parameter.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword;
}
exports.isThisParameter = isThisParameter;
function getModifier(node, kind) {
if (node.modifiers !== undefined)
for (const modifier of node.modifiers)
if (modifier.kind === kind)
return modifier;
}
exports.getModifier = getModifier;
function hasModifier(modifiers, ...kinds) {
if (modifiers === undefined)
return false;
for (const modifier of modifiers)
if (kinds.includes(modifier.kind))
return true;
return false;
}
exports.hasModifier = hasModifier;
function isParameterProperty(node) {
return hasModifier(node.modifiers, ts.SyntaxKind.PublicKeyword, ts.SyntaxKind.ProtectedKeyword, ts.SyntaxKind.PrivateKeyword, ts.SyntaxKind.ReadonlyKeyword);
}
exports.isParameterProperty = isParameterProperty;
function hasAccessModifier(node) {
return isModifierFlagSet(node, ts.ModifierFlags.AccessibilityModifier);
}
exports.hasAccessModifier = hasAccessModifier;
function isFlagSet(obj, flag) {
return (obj.flags & flag) !== 0;
}
exports.isNodeFlagSet = isFlagSet;
exports.isTypeFlagSet = isFlagSet;
exports.isSymbolFlagSet = isFlagSet;
function isObjectFlagSet(objectType, flag) {
return (objectType.objectFlags & flag) !== 0;
}
exports.isObjectFlagSet = isObjectFlagSet;
function isModifierFlagSet(node, flag) {
return (ts.getCombinedModifierFlags(node) & flag) !== 0;
}
exports.isModifierFlagSet = isModifierFlagSet;
function getPreviousStatement(statement) {
const parent = statement.parent;
if (node_1.isBlockLike(parent)) {
const index = parent.statements.indexOf(statement);
if (index > 0)
return parent.statements[index - 1];
}
}
exports.getPreviousStatement = getPreviousStatement;
function getNextStatement(statement) {
const parent = statement.parent;
if (node_1.isBlockLike(parent)) {
const index = parent.statements.indexOf(statement);
if (index < parent.statements.length)
return parent.statements[index + 1];
}
}
exports.getNextStatement = getNextStatement;
/** Returns the token before the start of `node` or `undefined` if there is none. */
function getPreviousToken(node, sourceFile) {
const { pos } = node;
if (pos === 0)
return;
do
node = node.parent;
while (node.pos === pos);
return getTokenAtPositionWorker(node, pos - 1, sourceFile !== null && sourceFile !== void 0 ? sourceFile : node.getSourceFile(), false);
}
exports.getPreviousToken = getPreviousToken;
/** Returns the next token that begins after the end of `node`. Returns `undefined` for SourceFile and EndOfFileToken */
function getNextToken(node, sourceFile) {
if (node.kind === ts.SyntaxKind.SourceFile || node.kind === ts.SyntaxKind.EndOfFileToken)
return;
const end = node.end;
node = node.parent;
while (node.end === end) {
if (node.parent === undefined)
return node.endOfFileToken;
node = node.parent;
}
return getTokenAtPositionWorker(node, end, sourceFile !== null && sourceFile !== void 0 ? sourceFile : node.getSourceFile(), false);
}
exports.getNextToken = getNextToken;
/** Returns the token at or following the specified position or undefined if none is found inside `parent`. */
function getTokenAtPosition(parent, pos, sourceFile, allowJsDoc) {
if (pos < parent.pos || pos >= parent.end)
return;
if (isTokenKind(parent.kind))
return parent;
return getTokenAtPositionWorker(parent, pos, sourceFile !== null && sourceFile !== void 0 ? sourceFile : parent.getSourceFile(), allowJsDoc === true);
}
exports.getTokenAtPosition = getTokenAtPosition;
function getTokenAtPositionWorker(node, pos, sourceFile, allowJsDoc) {
if (!allowJsDoc) {
// if we are not interested in JSDoc, we can skip to the deepest AST node at the given position
node = getAstNodeAtPosition(node, pos);
if (isTokenKind(node.kind))
return node;
}
outer: while (true) {
for (const child of node.getChildren(sourceFile)) {
if (child.end > pos && (allowJsDoc || child.kind !== ts.SyntaxKind.JSDocComment)) {
if (isTokenKind(child.kind))
return child;
// next token is nested in another node
node = child;
continue outer;
}
}
return;
}
}
/**
* Return the comment at the specified position.
* You can pass an optional `parent` to avoid some work finding the corresponding token starting at `sourceFile`.
* If the `parent` parameter is passed, `pos` must be between `parent.pos` and `parent.end`.
*/
function getCommentAtPosition(sourceFile, pos, parent = sourceFile) {
const token = getTokenAtPosition(parent, pos, sourceFile);
if (token === undefined || token.kind === ts.SyntaxKind.JsxText || pos >= token.end - (ts.tokenToString(token.kind) || '').length)
return;
const startPos = token.pos === 0
? (ts.getShebang(sourceFile.text) || '').length
: token.pos;
return startPos !== 0 && ts.forEachTrailingCommentRange(sourceFile.text, startPos, commentAtPositionCallback, pos) ||
ts.forEachLeadingCommentRange(sourceFile.text, startPos, commentAtPositionCallback, pos);
}
exports.getCommentAtPosition = getCommentAtPosition;
function commentAtPositionCallback(pos, end, kind, _nl, at) {
return at >= pos && at < end ? { pos, end, kind } : undefined;
}
/**
* Returns whether the specified position is inside a comment.
* You can pass an optional `parent` to avoid some work finding the corresponding token starting at `sourceFile`.
* If the `parent` parameter is passed, `pos` must be between `parent.pos` and `parent.end`.
*/
function isPositionInComment(sourceFile, pos, parent) {
return getCommentAtPosition(sourceFile, pos, parent) !== undefined;
}
exports.isPositionInComment = isPositionInComment;
function commentText(sourceText, comment) {
return sourceText.substring(comment.pos + 2, comment.kind === ts.SyntaxKind.SingleLineCommentTrivia ? comment.end : comment.end - 2);
}
exports.commentText = commentText;
/** Returns the deepest AST Node at `pos`. Returns undefined if `pos` is outside of the range of `node` */
function getAstNodeAtPosition(node, pos) {
if (node.pos > pos || node.end <= pos)
return;
while (isNodeKind(node.kind)) {
const nested = ts.forEachChild(node, (child) => child.pos <= pos && child.end > pos ? child : undefined);
if (nested === undefined)
break;
node = nested;
}
return node;
}
exports.getAstNodeAtPosition = getAstNodeAtPosition;
/**
* Returns the NodeWrap of deepest AST node that contains `pos` between its `pos` and `end`.
* Only returns undefined if pos is outside of `wrap`
*/
function getWrappedNodeAtPosition(wrap, pos) {
if (wrap.node.pos > pos || wrap.node.end <= pos)
return;
outer: while (true) {
for (const child of wrap.children) {
if (child.node.pos > pos)
return wrap;
if (child.node.end > pos) {
wrap = child;
continue outer;
}
}
return wrap;
}
}
exports.getWrappedNodeAtPosition = getWrappedNodeAtPosition;
function getPropertyName(propertyName) {
if (propertyName.kind === ts.SyntaxKind.ComputedPropertyName) {
const expression = unwrapParentheses(propertyName.expression);
if (node_1.isPrefixUnaryExpression(expression)) {
let negate = false;
switch (expression.operator) {
case ts.SyntaxKind.MinusToken:
negate = true;
// falls through
case ts.SyntaxKind.PlusToken:
return node_1.isNumericLiteral(expression.operand)
? `${negate ? '-' : ''}${expression.operand.text}`
: _3_2_1.isBigIntLiteral(expression.operand)
? `${negate ? '-' : ''}${expression.operand.text.slice(0, -1)}`
: undefined;
default:
return;
}
}
if (_3_2_1.isBigIntLiteral(expression))
// handle BigInt, even though TypeScript doesn't allow BigInt as computed property name
return expression.text.slice(0, -1);
if (node_1.isNumericOrStringLikeLiteral(expression))
return expression.text;
return;
}
return propertyName.kind === ts.SyntaxKind.PrivateIdentifier ? undefined : propertyName.text;
}
exports.getPropertyName = getPropertyName;
function forEachDestructuringIdentifier(pattern, fn) {
for (const element of pattern.elements) {
if (element.kind !== ts.SyntaxKind.BindingElement)
continue;
let result;
if (element.name.kind === ts.SyntaxKind.Identifier) {
result = fn(element);
}
else {
result = forEachDestructuringIdentifier(element.name, fn);
}
if (result)
return result;
}
}
exports.forEachDestructuringIdentifier = forEachDestructuringIdentifier;
function forEachDeclaredVariable(declarationList, cb) {
for (const declaration of declarationList.declarations) {
let result;
if (declaration.name.kind === ts.SyntaxKind.Identifier) {
result = cb(declaration);
}
else {
result = forEachDestructuringIdentifier(declaration.name, cb);
}
if (result)
return result;
}
}
exports.forEachDeclaredVariable = forEachDeclaredVariable;
var VariableDeclarationKind;
(function (VariableDeclarationKind) {
VariableDeclarationKind[VariableDeclarationKind["Var"] = 0] = "Var";
VariableDeclarationKind[VariableDeclarationKind["Let"] = 1] = "Let";
VariableDeclarationKind[VariableDeclarationKind["Const"] = 2] = "Const";
})(VariableDeclarationKind = exports.VariableDeclarationKind || (exports.VariableDeclarationKind = {}));
function getVariableDeclarationKind(declarationList) {
if (declarationList.flags & ts.NodeFlags.Let)
return 1 /* Let */;
if (declarationList.flags & ts.NodeFlags.Const)
return 2 /* Const */;
return 0 /* Var */;
}
exports.getVariableDeclarationKind = getVariableDeclarationKind;
function isBlockScopedVariableDeclarationList(declarationList) {
return (declarationList.flags & ts.NodeFlags.BlockScoped) !== 0;
}
exports.isBlockScopedVariableDeclarationList = isBlockScopedVariableDeclarationList;
function isBlockScopedVariableDeclaration(declaration) {
const parent = declaration.parent;
return parent.kind === ts.SyntaxKind.CatchClause ||
isBlockScopedVariableDeclarationList(parent);
}
exports.isBlockScopedVariableDeclaration = isBlockScopedVariableDeclaration;
function isBlockScopedDeclarationStatement(statement) {
switch (statement.kind) {
case ts.SyntaxKind.VariableStatement:
return isBlockScopedVariableDeclarationList(statement.declarationList);
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.TypeAliasDeclaration:
return true;
default:
return false;
}
}
exports.isBlockScopedDeclarationStatement = isBlockScopedDeclarationStatement;
function isInSingleStatementContext(statement) {
switch (statement.parent.kind) {
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.ForOfStatement:
case ts.SyntaxKind.WhileStatement:
case ts.SyntaxKind.DoStatement:
case ts.SyntaxKind.IfStatement:
case ts.SyntaxKind.WithStatement:
case ts.SyntaxKind.LabeledStatement:
return true;
default:
return false;
}
}
exports.isInSingleStatementContext = isInSingleStatementContext;
var ScopeBoundary;
(function (ScopeBoundary) {
ScopeBoundary[ScopeBoundary["None"] = 0] = "None";
ScopeBoundary[ScopeBoundary["Function"] = 1] = "Function";
ScopeBoundary[ScopeBoundary["Block"] = 2] = "Block";
ScopeBoundary[ScopeBoundary["Type"] = 4] = "Type";
ScopeBoundary[ScopeBoundary["ConditionalType"] = 8] = "ConditionalType";
})(ScopeBoundary = exports.ScopeBoundary || (exports.ScopeBoundary = {}));
var ScopeBoundarySelector;
(function (ScopeBoundarySelector) {
ScopeBoundarySelector[ScopeBoundarySelector["Function"] = 1] = "Function";
ScopeBoundarySelector[ScopeBoundarySelector["Block"] = 3] = "Block";
ScopeBoundarySelector[ScopeBoundarySelector["Type"] = 7] = "Type";
ScopeBoundarySelector[ScopeBoundarySelector["InferType"] = 8] = "InferType";
})(ScopeBoundarySelector = exports.ScopeBoundarySelector || (exports.ScopeBoundarySelector = {}));
function isScopeBoundary(node) {
return isFunctionScopeBoundary(node) || isBlockScopeBoundary(node) || isTypeScopeBoundary(node);
}
exports.isScopeBoundary = isScopeBoundary;
function isTypeScopeBoundary(node) {
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.MappedType:
return 4 /* Type */;
case ts.SyntaxKind.ConditionalType:
return 8 /* ConditionalType */;
default:
return 0 /* None */;
}
}
exports.isTypeScopeBoundary = isTypeScopeBoundary;
function isFunctionScopeBoundary(node) {
switch (node.kind) {
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.Constructor:
case ts.SyntaxKind.ModuleDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.ClassExpression:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.CallSignature:
case ts.SyntaxKind.ConstructSignature:
case ts.SyntaxKind.ConstructorType:
case ts.SyntaxKind.FunctionType:
return 1 /* Function */;
case ts.SyntaxKind.SourceFile:
// if SourceFile is no module, it contributes to the global scope and is therefore no scope boundary
return ts.isExternalModule(node) ? 1 /* Function */ : 0 /* None */;
default:
return 0 /* None */;
}
}
exports.isFunctionScopeBoundary = isFunctionScopeBoundary;
function isBlockScopeBoundary(node) {
switch (node.kind) {
case ts.SyntaxKind.Block:
const parent = node.parent;
return parent.kind !== ts.SyntaxKind.CatchClause &&
// blocks inside SourceFile are block scope boundaries
(parent.kind === ts.SyntaxKind.SourceFile ||
// blocks that are direct children of a function scope boundary are no scope boundary
// for example the FunctionBlock is part of the function scope of the containing function
!isFunctionScopeBoundary(parent))
? 2 /* Block */
: 0 /* None */;
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.ForOfStatement:
case ts.SyntaxKind.CaseBlock:
case ts.SyntaxKind.CatchClause:
case ts.SyntaxKind.WithStatement:
return 2 /* Block */;
default:
return 0 /* None */;
}
}
exports.isBlockScopeBoundary = isBlockScopeBoundary;
/** Returns true for scope boundaries that have their own `this` reference instead of inheriting it from the containing scope */
function hasOwnThisReference(node) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.ClassExpression:
case ts.SyntaxKind.FunctionExpression:
return true;
case ts.SyntaxKind.FunctionDeclaration:
return node.body !== undefined;
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
return node.parent.kind === ts.SyntaxKind.ObjectLiteralExpression;
default:
return false;
}
}
exports.hasOwnThisReference = hasOwnThisReference;
function isFunctionWithBody(node) {
switch (node.kind) {
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.Constructor:
return node.body !== undefined;
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction:
return true;
default:
return false;
}
}
exports.isFunctionWithBody = isFunctionWithBody;
/**
* Iterate over all tokens of `node`
*
* @param node The node whose tokens should be visited
* @param cb Is called for every token contained in `node`
*/
function forEachToken(node, cb, sourceFile = node.getSourceFile()) {
const queue = [];
while (true) {
if (isTokenKind(node.kind)) {
cb(node);
}
else if (node.kind !== ts.SyntaxKind.JSDocComment) {
const children = node.getChildren(sourceFile);
if (children.length === 1) {
node = children[0];
continue;
}
for (let i = children.length - 1; i >= 0; --i)
queue.push(children[i]); // add children in reverse order, when we pop the next element from the queue, it's the first child
}
if (queue.length === 0)
break;
node = queue.pop();
}
}
exports.forEachToken = forEachToken;
/**
* Iterate over all tokens and trivia of `node`
*
* @description JsDoc comments are treated like regular comments
*
* @param node The node whose tokens should be visited
* @param cb Is called for every token contained in `node` and trivia before the token
*/
function forEachTokenWithTrivia(node, cb, sourceFile = node.getSourceFile()) {
const fullText = sourceFile.text;
const scanner = ts.createScanner(sourceFile.languageVersion, false, sourceFile.languageVariant, fullText);
return forEachToken(node, (token) => {
const tokenStart = token.kind === ts.SyntaxKind.JsxText || token.pos === token.end ? token.pos : token.getStart(sourceFile);
if (tokenStart !== token.pos) {
// we only have to handle trivia before each token. whitespace at the end of the file is followed by EndOfFileToken
scanner.setTextPos(token.pos);
let kind = scanner.scan();
let pos = scanner.getTokenPos();
while (pos < tokenStart) {
const textPos = scanner.getTextPos();
cb(fullText, kind, { pos, end: textPos }, token.parent);
if (textPos === tokenStart)
break;
kind = scanner.scan();
pos = scanner.getTokenPos();
}
}
return cb(fullText, token.kind, { end: token.end, pos: tokenStart }, token.parent);
}, sourceFile);
}
exports.forEachTokenWithTrivia = forEachTokenWithTrivia;
/** Iterate over all comments owned by `node` or its children */
function forEachComment(node, cb, sourceFile = node.getSourceFile()) {
/* Visit all tokens and skip trivia.
Comment ranges between tokens are parsed without the need of a scanner.
forEachTokenWithWhitespace 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*/
const fullText = sourceFile.text;
const notJsx = sourceFile.languageVariant !== ts.LanguageVariant.JSX;
return forEachToken(node, (token) => {
if (token.pos === token.end)
return;
if (token.kind !== ts.SyntaxKind.JsxText)
ts.forEachLeadingCommentRange(fullText,
// skip shebang at position 0
token.pos === 0 ? (ts.getShebang(fullText) || '').length : token.pos, commentCallback);
if (notJsx || canHaveTrailingTrivia(token))
return ts.forEachTrailingCommentRange(fullText, token.end, commentCallback);
}, sourceFile);
function commentCallback(pos, end, kind) {
cb(fullText, { pos, end, kind });
}
}
exports.forEachComment = forEachComment;
/** Exclude trailing positions that would lead to scanning for trivia inside JsxText */
function canHaveTrailingTrivia(token) {
switch (token.kind) {
case ts.SyntaxKind.CloseBraceToken:
// after a JsxExpression inside a JsxElement's body can only be other JsxChild, but no trivia
return token.parent.kind !== ts.SyntaxKind.JsxExpression || !isJsxElementOrFragment(token.parent.parent);
case ts.SyntaxKind.GreaterThanToken:
switch (token.parent.kind) {
case ts.SyntaxKind.JsxOpeningElement:
// if end is not equal, this is part of the type arguments list. in all other cases it would be inside the element body
return token.end !== token.parent.end;
case ts.SyntaxKind.JsxOpeningFragment:
return false; // would be inside the fragment
case ts.SyntaxKind.JsxSelfClosingElement:
return token.end !== token.parent.end || // if end is not equal, this is part of the type arguments list
!isJsxElementOrFragment(token.parent.parent); // there's only trailing trivia if it's the end of the top element
case ts.SyntaxKind.JsxClosingElement:
case ts.SyntaxKind.JsxClosingFragment:
// there's only trailing trivia if it's the end of the top element
return !isJsxElementOrFragment(token.parent.parent.parent);
}
}
return true;
}
function isJsxElementOrFragment(node) {
return node.kind === ts.SyntaxKind.JsxElement || node.kind === ts.SyntaxKind.JsxFragment;
}
function getLineRanges(sourceFile) {
const lineStarts = sourceFile.getLineStarts();
const result = [];
const length = lineStarts.length;
const sourceText = sourceFile.text;
let pos = 0;
for (let i = 1; i < length; ++i) {
const end = lineStarts[i];
let lineEnd = end;
for (; lineEnd > pos; --lineEnd)
if (!ts.isLineBreak(sourceText.charCodeAt(lineEnd - 1)))
break;
result.push({
pos,
end,
contentLength: lineEnd - pos,
});
pos = end;
}
result.push({
pos,
end: sourceFile.end,
contentLength: sourceFile.end - pos,
});
return result;
}
exports.getLineRanges = getLineRanges;
/** Get the line break style used in sourceFile. This function only looks at the first line break. If there is none, \n is assumed. */
function getLineBreakStyle(sourceFile) {
const lineStarts = sourceFile.getLineStarts();
return lineStarts.length === 1 || lineStarts[1] < 2 || sourceFile.text[lineStarts[1] - 2] !== '\r'
? '\n'
: '\r\n';
}
exports.getLineBreakStyle = getLineBreakStyle;
let cachedScanner;
function scanToken(text, languageVersion) {
if (cachedScanner === undefined) {
// cache scanner
cachedScanner = ts.createScanner(languageVersion, false, undefined, text);
}
else {
cachedScanner.setScriptTarget(languageVersion);
cachedScanner.setText(text);
}
cachedScanner.scan();
return cachedScanner;
}
/**
* Determines whether the given text parses as a standalone identifier.
* This is not a guarantee that it works in every context. The property name in PropertyAccessExpressions for example allows reserved words.
* Depending on the context it could be parsed as contextual keyword or TypeScript keyword.
*/
function isValidIdentifier(text, languageVersion = ts.ScriptTarget.Latest) {
const scan = scanToken(text, languageVersion);
return scan.isIdentifier() && scan.getTextPos() === text.length && scan.getTokenPos() === 0;
}
exports.isValidIdentifier = isValidIdentifier;
function charSize(ch) {
return ch >= 0x10000 ? 2 : 1;
}
/**
* Determines whether the given text can be used to access a property with a PropertyAccessExpression while preserving the property's name.
*/
function isValidPropertyAccess(text, languageVersion = ts.ScriptTarget.Latest) {
if (text.length === 0)
return false;
let ch = text.codePointAt(0);
if (!ts.isIdentifierStart(ch, languageVersion))
return false;
for (let i = charSize(ch); i < text.length; i += charSize(ch)) {
ch = text.codePointAt(i);
if (!ts.isIdentifierPart(ch, languageVersion))
return false;
}
return true;
}
exports.isValidPropertyAccess = isValidPropertyAccess;
/**
* Determines whether the given text can be used as unquoted name of a property declaration while preserving the property's name.
*/
function isValidPropertyName(text, languageVersion = ts.ScriptTarget.Latest) {
if (isValidPropertyAccess(text, languageVersion))
return true;
const scan = scanToken(text, languageVersion);
return scan.getTextPos() === text.length &&
scan.getToken() === ts.SyntaxKind.NumericLiteral && scan.getTokenValue() === text; // ensure stringified number equals literal
}
exports.isValidPropertyName = isValidPropertyName;
/**
* Determines whether the given text can be parsed as a numeric literal.
*/
function isValidNumericLiteral(text, languageVersion = ts.ScriptTarget.Latest) {
const scan = scanToken(text, languageVersion);
return scan.getToken() === ts.SyntaxKind.NumericLiteral && scan.getTextPos() === text.length && scan.getTokenPos() === 0;
}
exports.isValidNumericLiteral = isValidNumericLiteral;
/**
* Determines whether the given text can be used as JSX tag or attribute name while preserving the exact name.
*/
function isValidJsxIdentifier(text, languageVersion = ts.ScriptTarget.Latest) {
if (text.length === 0)
return false;
let seenNamespaceSeparator = false;
let ch = text.codePointAt(0);
if (!ts.isIdentifierStart(ch, languageVersion))
return false;
for (let i = charSize(ch); i < text.length; i += charSize(ch)) {
ch = text.codePointAt(i);
if (!ts.isIdentifierPart(ch, languageVersion) && ch !== 45 /* minus */) {
if (!seenNamespaceSeparator && ch === 58 /* colon */ && i + charSize(ch) !== text.length) {
seenNamespaceSeparator = true;
}
else {
return false;
}
}
}
return true;
}
exports.isValidJsxIdentifier = isValidJsxIdentifier;
function isNumericPropertyName(name) {
return String(+name) === name;
}
exports.isNumericPropertyName = isNumericPropertyName;
function isSameLine(sourceFile, pos1, pos2) {
return ts.getLineAndCharacterOfPosition(sourceFile, pos1).line === ts.getLineAndCharacterOfPosition(sourceFile, pos2).line;
}
exports.isSameLine = isSameLine;
var SideEffectOptions;
(function (SideEffectOptions) {
SideEffectOptions[SideEffectOptions["None"] = 0] = "None";
SideEffectOptions[SideEffectOptions["TaggedTemplate"] = 1] = "TaggedTemplate";
SideEffectOptions[SideEffectOptions["Constructor"] = 2] = "Constructor";
SideEffectOptions[SideEffectOptions["JsxElement"] = 4] = "JsxElement";
})(SideEffectOptions = exports.SideEffectOptions || (exports.SideEffectOptions = {}));
function hasSideEffects(node, options) {
var _a, _b;
const queue = [];
while (true) {
switch (node.kind) {
case ts.SyntaxKind.CallExpression:
case ts.SyntaxKind.PostfixUnaryExpression:
case ts.SyntaxKind.AwaitExpression:
case ts.SyntaxKind.YieldExpression:
case ts.SyntaxKind.DeleteExpression:
return true;
case ts.SyntaxKind.TypeAssertionExpression:
case ts.SyntaxKind.AsExpression:
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.NonNullExpression:
case ts.SyntaxKind.VoidExpression:
case ts.SyntaxKind.TypeOfExpression:
case ts.SyntaxKind.PropertyAccessExpression:
case ts.SyntaxKind.SpreadElement:
case ts.SyntaxKind.PartiallyEmittedExpression:
node = node.expression;
continue;
case ts.SyntaxKind.BinaryExpression:
if (isAssignmentKind(node.operatorToken.kind))
return true;
queue.push(node.right);
node = node.left;
continue;
case ts.SyntaxKind.PrefixUnaryExpression:
switch (node.operator) {
case ts.SyntaxKind.PlusPlusToken:
case ts.SyntaxKind.MinusMinusToken:
return true;
default:
node = node.operand;
continue;
}
case ts.SyntaxKind.ElementAccessExpression:
if (node.argumentExpression !== undefined) // for compatibility with typescript@<2.9.0
queue.push(node.argumentExpression);
node = node.expression;
continue;
case ts.SyntaxKind.ConditionalExpression:
queue.push(node.whenTrue, node.whenFalse);
node = node.condition;
continue;
case ts.SyntaxKind.NewExpression:
if (options & 2 /* Constructor */)
return true;
if (node.arguments !== undefined)
queue.push(...node.arguments);
node = node.expression;
continue;
case ts.SyntaxKind.TaggedTemplateExpression:
if (options & 1 /* TaggedTemplate */)
return true;
queue.push(node.tag);
node = node.template;
if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral)
break;
// falls through
case ts.SyntaxKind.TemplateExpression:
for (const child of node.templateSpans)
queue.push(child.expression);
break;
case ts.SyntaxKind.ClassExpression: {
if (node.decorators !== undefined)
return true;
for (const child of node.members) {
if (child.decorators !== undefined)
return true;
if (!hasModifier(child.modifiers, ts.SyntaxKind.DeclareKeyword)) {
if (((_a = child.name) === null || _a === void 0 ? void 0 : _a.kind) === ts.SyntaxKind.ComputedPropertyName)
queue.push(child.name.expression);
if (node_1.isMethodDeclaration(child)) {
for (const p of child.parameters)
if (p.decorators !== undefined)
return true;
}
else if (node_1.isPropertyDeclaration(child) &&
child.initializer !== undefined &&
hasModifier(child.modifiers, ts.SyntaxKind.StaticKeyword)) {
queue.push(child.initializer);
}
}
}
const base = getBaseOfClassLikeExpression(node);
if (base === undefined)
break;
node = base.expression;
continue;
}
case ts.SyntaxKind.ArrayLiteralExpression:
queue.push(...node.elements);
break;
case ts.SyntaxKind.ObjectLiteralExpression:
for (const child of node.properties) {
if (((_b = child.name) === null || _b === void 0 ? void 0 : _b.kind) === ts.SyntaxKind.ComputedPropertyName)
queue.push(child.name.expression);
switch (child.kind) {
case ts.SyntaxKind.PropertyAssignment:
queue.push(child.initializer);
break;
case ts.SyntaxKind.SpreadAssignment:
queue.push(child.expression);
}
}
break;
case ts.SyntaxKind.JsxExpression:
if (node.expression === undefined)
break;
node = node.expression;
continue;
case ts.SyntaxKind.JsxElement:
case ts.SyntaxKind.JsxFragment:
for (const child of node.children)
if (child.kind !== ts.SyntaxKind.JsxText)
queue.push(child);
if (node.kind === ts.SyntaxKind.JsxFragment)
break;
node = node.openingElement;
// falls through
case ts.SyntaxKind.JsxSelfClosingElement:
case ts.SyntaxKind.JsxOpeningElement:
if (options & 4 /* JsxElement */)
return true;
for (const child of node.attributes.properties) {
if (child.kind === ts.SyntaxKind.JsxSpreadAttribute) {
queue.push(child.expression);
}
else if (child.initializer !== undefined) {
queue.push(child.initializer);
}
}
break;
case ts.SyntaxKind.CommaListExpression:
queue.push(...node.elements);
}
if (queue.length === 0)
return false;
node = queue.pop();
}
}
exports.hasSideEffects = hasSideEffects;
/** Returns the VariableDeclaration or ParameterDeclaration that contains the BindingElement */
function getDeclarationOfBindingElement(node) {
let parent = node.parent.parent;
while (parent.kind === ts.SyntaxKind.BindingElement)
parent = parent.parent.parent;
return parent;
}
exports.getDeclarationOfBindingElement = getDeclarationOfBindingElement;
function isExpressionValueUsed(node) {
while (true) {
const parent = node.parent;
switch (parent.kind) {
case ts.SyntaxKind.CallExpression:
case ts.SyntaxKind.NewExpression:
case ts.SyntaxKind.ElementAccessExpression:
case ts.SyntaxKind.WhileStatement:
case ts.SyntaxKind.DoStatement:
case ts.SyntaxKind.WithStatement:
case ts.SyntaxKind.ThrowStatement:
case ts.SyntaxKind.ReturnStatement:
case ts.SyntaxKind.JsxExpression:
case ts.SyntaxKind.JsxSpreadAttribute:
case ts.SyntaxKind.JsxElement:
case ts.SyntaxKind.JsxFragment:
case ts.SyntaxKind.JsxSelfClosingElement:
case ts.SyntaxKind.ComputedPropertyName:
case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.ExportSpecifier:
case ts.SyntaxKind.ExportAssignment:
case ts.SyntaxKind.ImportDeclaration:
case ts.SyntaxKind.ExternalModuleReference:
case ts.SyntaxKind.Decorator:
case ts.SyntaxKind.TaggedTemplateExpression:
case ts.SyntaxKind.TemplateSpan:
case ts.SyntaxKind.ExpressionWithTypeArguments:
case ts.SyntaxKind.TypeOfExpression:
case ts.SyntaxKind.AwaitExpression:
case ts.SyntaxKind.YieldExpression:
case ts.SyntaxKind.LiteralType:
case ts.SyntaxKind.JsxAttributes:
case ts.SyntaxKind.JsxOpeningElement:
case ts.SyntaxKind.JsxClosingElement:
case ts.SyntaxKind.IfStatement:
case ts.SyntaxKind.CaseClause:
case ts.SyntaxKind.SwitchStatement:
return true;
case ts.SyntaxKind.PropertyAccessExpression:
return parent.expression === node;
case ts.SyntaxKind.QualifiedName:
return parent.left === node;
case ts.SyntaxKind.ShorthandPropertyAssignment:
return parent.objectAssignmentInitializer === node ||
!isInDestructuringAssignment(parent);
case ts.SyntaxKind.PropertyAssignment:
return parent.initializer === node && !isInDestructuringAssignment(parent);
case ts.SyntaxKind.SpreadAssignment:
case ts.SyntaxKind.SpreadElement:
case ts.SyntaxKind.ArrayLiteralExpression:
return !isInDestructuringAssignment(parent);
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.AsExpression:
case ts.SyntaxKind.TypeAssertionExpression:
case ts.SyntaxKind.PostfixUnaryExpression:
case ts.SyntaxKind.PrefixUnaryExpression:
case ts.SyntaxKind.NonNullExpression:
node = parent;
continue;
case ts.SyntaxKind.ForStatement:
return parent.condition === node;
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.ForOfStatement:
return parent.expression === node;
case ts.SyntaxKind.ConditionalExpression:
if (parent.condition === node)
return true;
node = parent;
break;
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.BindingElement:
case ts.SyntaxKind.VariableDeclaration:
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.EnumMember:
return parent.initializer === node;
case ts.SyntaxKind.ImportEqualsDeclaration:
return parent.moduleReference === node;
case ts.SyntaxKind.CommaListExpression:
if (parent.elements[parent.elements.length - 1] !== node)
return false;
node = parent;
break;
case ts.SyntaxKind.BinaryExpression:
if (parent.right === node) {
if (parent.operatorToken.kind === ts.SyntaxKind.CommaToken) {
node = parent;
break;
}
return true;
}
switch (parent.operatorToken.kind) {
case ts.SyntaxKind.CommaToken:
case ts.SyntaxKind.EqualsToken:
return false;
case ts.SyntaxKind.EqualsEqualsEqualsToken:
case ts.SyntaxKind.EqualsEqualsToken:
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
case ts.SyntaxKind.ExclamationEqualsToken:
case ts.SyntaxKind.InstanceOfKeyword:
case ts.SyntaxKind.PlusToken:
case ts.SyntaxKind.MinusToken:
case ts.SyntaxKind.AsteriskToken:
case ts.SyntaxKind.SlashToken:
case ts.SyntaxKind.PercentToken:
case ts.SyntaxKind.AsteriskAsteriskToken:
case ts.SyntaxKind.GreaterThanToken:
case ts.SyntaxKind.GreaterThanGreaterThanToken:
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case ts.SyntaxKind.GreaterThanEqualsToken:
case ts.SyntaxKind.LessThanToken:
case ts.SyntaxKind.LessThanLessThanToken:
case ts.SyntaxKind.LessThanEqualsToken:
case ts.SyntaxKind.AmpersandToken:
case ts.SyntaxKind.BarToken:
case ts.SyntaxKind.CaretToken:
case ts.SyntaxKind.BarBarToken:
case ts.SyntaxKind.AmpersandAmpersandToken:
case ts.SyntaxKind.QuestionQuestionToken:
case ts.SyntaxKind.InKeyword:
case ts.SyntaxKind.QuestionQuestionEqualsToken:
case ts.SyntaxKind.AmpersandAmpersandEqualsToken:
case ts.SyntaxKind.BarBarEqualsToken:
return true;
default:
node = parent;
}
break;
default:
return false;
}
}
}
exports.isExpressionValueUsed = isExpressionValueUsed;
function isInDestructuringAssignment(node) {
switch (node.kind) {
case ts.SyntaxKind.ShorthandPropertyAssignment:
if (node.objectAssignmentInitializer !== undefined)
return true;
// falls through
case ts.SyntaxKind.PropertyAssignment:
case ts.SyntaxKind.SpreadAssignment:
node = node.parent;
break;
case ts.SyntaxKind.SpreadElement:
if (node.parent.kind !== ts.SyntaxKind.ArrayLiteralExpression)
return false;
node = node.parent;
}
while (true) {
switch (node.parent.kind) {
case ts.SyntaxKind.BinaryExpression:
return node.parent.left === node &&
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
case ts.SyntaxKind.ForOfStatement:
return node.parent.initializer === node;
case ts.SyntaxKind.ArrayLiteralExpression:
case ts.SyntaxKind.ObjectLiteralExpression:
node = node.parent;
break;
case ts.SyntaxKind.SpreadAssignment:
case ts.SyntaxKind.PropertyAssignment:
node = node.parent.parent;
break;
case ts.SyntaxKind.SpreadElement:
if (node.parent.parent.kind !== ts.SyntaxKind.ArrayLiteralExpression)
return false;
node = node.parent.parent;
break;
default:
return false;
}
}
}
var AccessKind;
(function (AccessKind) {
AccessKind[AccessKind["None"] = 0] = "None";
AccessKind[AccessKind["Read"] = 1] = "Read";
AccessKind[AccessKind["Write"] = 2] = "Write";
AccessKind[AccessKind["Delete"] = 4] = "Delete";
AccessKind[AccessKind["ReadWrite"] = 3] = "ReadWrite";
AccessKind[AccessKind["Modification"] = 6] = "Modification";
})(AccessKind = exports.AccessKind || (exports.AccessKind = {}));
function getAccessKind(node) {
const parent = node.parent;
switch (parent.kind) {
case ts.SyntaxKind.DeleteExpression:
return 4 /* Delete */;
case ts.SyntaxKind.PostfixUnaryExpression:
return 3 /* ReadWrite */;
case ts.SyntaxKind.PrefixUnaryExpression:
return parent.operator === ts.SyntaxKind.PlusPlusToken ||
parent.operator === ts.SyntaxKind.MinusMinusToken
? 3 /* ReadWrite */
: 1 /* Read */;
case ts.SyntaxKind.BinaryExpression:
return parent.right === node
? 1 /* Read */
: !isAssignmentKind(parent.operatorToken.kind)
? 1 /* Read */
: parent.operatorToken.kind === ts.SyntaxKind.EqualsToken
? 2 /* Write */
: 3 /* ReadWrite */;
case ts.SyntaxKind.ShorthandPropertyAssignment:
return parent.objectAssignmentInitializer === node
? 1 /* Read */
: isInDestructuringAssignment(parent)
? 2 /* Write */
: 1 /* Read */;
case ts.SyntaxKind.PropertyAssignment:
return parent.name === node
? 0 /* None */
: isInDestructuringAssignment(parent)
? 2 /* Write */
: 1 /* Read */;
case ts.SyntaxKind.ArrayLiteralExpression:
case ts.SyntaxKind.SpreadElement:
case ts.SyntaxKind.SpreadAssignment:
return isInDestructuringAssignment(parent)
? 2 /* Write */
: 1 /* Read */;
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.NonNullExpression:
case ts.SyntaxKind.TypeAssertionExpression:
case ts.SyntaxKind.AsExpression:
// (<number>foo! as {})++
return getAccessKind(parent);
case ts.SyntaxKind.ForOfStatement:
case ts.SyntaxKind.ForInStatement:
return parent.initializer === node
? 2 /* Write */
: 1 /* Read */;
case ts.SyntaxKind.ExpressionWithTypeArguments:
return parent.parent.token === ts.SyntaxKind.ExtendsKeyword &&
parent.parent.parent.kind !== ts.SyntaxKind.InterfaceDeclaration
? 1 /* Read */
: 0 /* None */;
case ts.SyntaxKind.ComputedPropertyName:
case ts.SyntaxKind.ExpressionStatement:
case ts.SyntaxKind.TypeOfExpression:
case ts.SyntaxKind.ElementAccessExpression:
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.IfStatement:
case ts.SyntaxKind.DoStatement:
case ts.SyntaxKind.WhileStatement:
case ts.SyntaxKind.SwitchStatement:
case ts.SyntaxKind.WithStatement:
case ts.SyntaxKind.ThrowStatement:
case ts.SyntaxKind.CallExpression:
case ts.SyntaxKind.NewExpression:
case ts.SyntaxKind.TaggedTemplateExpression:
case ts.SyntaxKind.JsxExpression:
case ts.SyntaxKind.Decorator:
case ts.SyntaxKind.TemplateSpan:
case ts.SyntaxKind.JsxOpeningElement:
case ts.SyntaxKind.JsxSelfClosingElement:
case ts.SyntaxKind.JsxSpreadAttribute:
case ts.SyntaxKind.VoidExpression:
case ts.SyntaxKind.ReturnStatement:
case ts.SyntaxKind.AwaitExpression:
case ts.SyntaxKind.YieldExpression:
case ts.SyntaxKind.ConditionalExpression:
case ts.SyntaxKind.CaseClause:
case ts.SyntaxKind.JsxElement:
return 1 /* Read */;
case ts.SyntaxKind.ArrowFunction:
return parent.body === node
? 1 /* Read */
: 2 /* Write */;
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.VariableDeclaration:
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.EnumMember:
case ts.SyntaxKind.BindingElement:
case ts.SyntaxKind.JsxAttribute:
return parent.initializer === node
? 1 /* Read */
: 0 /* None */;
case ts.SyntaxKind.PropertyAccessExpression:
return parent.expression === node
? 1 /* Read */
: 0 /* None */;
case ts.SyntaxKind.ExportAssignment:
return parent.isExportEquals
? 1 /* Read */
: 0 /* None */;
}
return 0 /* None */;
}
exports.getAccessKind = getAccessKind;
function isReassignmentTarget(node) {
return (getAccessKind(node) & 2 /* Write */) !== 0;
}
exports.isReassignmentTarget = isReassignmentTarget;
function canHaveJsDoc(node) {
const kind = node.kind;
switch (kind) {
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.CallSignature:
case ts.SyntaxKind.ConstructSignature:
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.PropertySignature:
case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.SpreadAssignment:
case ts.SyntaxKind.ShorthandPropertyAssignment:
case ts.SyntaxKind.PropertyAssignment:
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.LabeledStatement:
case ts.SyntaxKind.ExpressionStatement:
case ts.SyntaxKind.VariableStatement:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.Constructor:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.ClassExpression:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.EnumMember:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.ModuleDeclaration:
case ts.SyntaxKind.ImportEqualsDeclaration:
case ts.SyntaxKind.ImportDeclaration:
case ts.SyntaxKind.NamespaceExportDeclaration:
case ts.SyntaxKind.ExportAssignment:
case ts.SyntaxKind.IndexSignature:
case ts.SyntaxKind.FunctionType:
case ts.SyntaxKind.ConstructorType:
case ts.SyntaxKind.JSDocFunctionType:
case ts.SyntaxKind.ExportDeclaration:
case ts.SyntaxKind.NamedTupleMember:
case ts.SyntaxKind.EndOfFileToken:
return true;
default:
return false;
}
}
exports.canHaveJsDoc = canHaveJsDoc;
/** Gets the JSDoc of a node. For performance reasons this function should only be called when `canHaveJsDoc` returns true. */
function getJsDoc(node, sourceFile) {
const result = [];
for (const child of node.getChildren(sourceFile)) {
if (!node_1.isJsDoc(child))
break;
result.push(child);
}
return result;
}
exports.getJsDoc = getJsDoc;
/**
* Parses the JsDoc of any node. This function is made for nodes that don't get their JsDoc parsed by the TypeScript parser.
*
* @param considerTrailingComments When set to `true` this function uses the trailing comments if the node starts on the same line
* as the previous node ends.
*/
function parseJsDocOfNode(node, considerTrailingComments, sourceFile = node.getSourceFile()) {
if (canHaveJsDoc(node) && node.kind !== ts.SyntaxKind.EndOfFileToken) {
const result = getJsDoc(node, sourceFile);
if (result.length !== 0 || !considerTrailingComments)
return result;
}
return parseJsDocWorker(node, node.getStart(sourceFile), sourceFile, considerTrailingComments);
}
exports.parseJsDocOfNode = parseJsDocOfNode;
function parseJsDocWorker(node, nodeStart, sourceFile, considerTrailingComments) {
const start = ts[considerTrailingComments && isSameLine(sourceFile, node.pos, nodeStart)
? 'forEachTrailingCommentRange'
: 'forEachLeadingCommentRange'](sourceFile.text, node.pos,
// return object to make `0` a truthy value
(pos, _end, kind) => kind === ts.SyntaxKind.MultiLineCommentTrivia && sourceFile.text[pos + 2] === '*' ? { pos } : undefined);
if (start === undefined)
return [];
const startPos = start.pos;
const text = sourceFile.text.slice(startPos, nodeStart);
const newSourceFile = ts.createSourceFile('jsdoc.ts', `${text}var a;`, sourceFile.languageVersion);
const result = getJsDoc(newSourceFile.statements[0], newSourceFile);
for (const doc of result)
updateNode(doc, node);
return result;
function updateNode(n, parent) {
n.pos += startPos;
n.end += startPos;
n.parent = parent;
return ts.forEachChild(n, (child) => updateNode(child, n), (children) => {
children.pos += startPos;
children.end += startPos;
for (const child of children)
updateNode(child, n);
});
}
}
var ImportKind;
(function (ImportKind) {
ImportKind[ImportKind["ImportDeclaration"] = 1] = "ImportDeclaration";
ImportKind[ImportKind["ImportEquals"] = 2] = "ImportEquals";
ImportKind[ImportKind["ExportFrom"] = 4] = "ExportFrom";
ImportKind[ImportKind["DynamicImport"] = 8] = "DynamicImport";
ImportKind[ImportKind["Require"] = 16] = "Require";
ImportKind[ImportKind["ImportType"] = 32] = "ImportType";
ImportKind[ImportKind["All"] = 63] = "All";
ImportKind[ImportKind["AllImports"] = 59] = "AllImports";
ImportKind[ImportKind["AllStaticImports"] = 3] = "AllStaticImports";
ImportKind[ImportKind["AllImportExpressions"] = 24] = "AllImportExpressions";
ImportKind[ImportKind["AllRequireLike"] = 18] = "AllRequireLike";
// @internal
ImportKind[ImportKind["AllNestedImports"] = 56] = "AllNestedImports";
// @internal
ImportKind[ImportKind["AllTopLevelImports"] = 7] = "AllTopLevelImports";
})(ImportKind = exports.ImportKind || (exports.ImportKind = {}));
function findImports(sourceFile, kinds, ignoreFileName = true) {
const result = [];
for (const node of findImportLikeNodes(sourceFile, kinds, ignoreFileName)) {
switch (node.kind) {
case ts.SyntaxKind.ImportDeclaration:
addIfTextualLiteral(node.moduleSpecifier);
break;
case ts.SyntaxKind.ImportEqualsDeclaration:
addIfTextualLiteral(node.moduleReference.expression);
break;
case ts.SyntaxKind.ExportDeclaration:
addIfTextualLiteral(node.moduleSpecifier);
break;
case ts.SyntaxKind.CallExpression:
addIfTextualLiteral(node.arguments[0]);
break;
case ts.SyntaxKind.ImportType:
if (node_1.isLiteralTypeNode(node.argument))
addIfTextualLiteral(node.argument.literal);
break;
default:
throw new Error('unexpected node');
}
}
return result;
function addIfTextualLiteral(node) {
if (node_1.isTextualLiteral(node))
result.push(node);
}
}
exports.findImports = findImports;
function findImportLikeNodes(sourceFile, kinds, ignoreFileName = true) {
return new ImportFinder(sourceFile, kinds, ignoreFileName).find();
}
exports.findImportLikeNodes = findImportLikeNodes;
class ImportFinder {
constructor(_sourceFile, _options, _ignoreFileName) {
this._sourceFile = _sourceFile;
this._options = _options;
this._ignoreFileName = _ignoreFileName;
this._result = [];
}
find() {
if (this._sourceFile.isDeclarationFile)
this._options &= ~24 /* AllImportExpressions */;
if (this._options & 7 /* AllTopLevelImports */)
this._findImports(this._sourceFile.statements);
if (this._options & 56 /* AllNestedImports */)
this._findNestedImports();
return this._result;
}
_findImports(statements) {
for (const statement of statements) {
if (node_1.isImportDeclaration(statement)) {
if (this._options & 1 /* ImportDeclaration */)
this._result.push(statement);
}
else if (node_1.isImportEqualsDeclaration(statement)) {
if (this._options & 2 /* ImportEquals */ &&
statement.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference)
this._result.push(statement);
}
else if (node_1.isExportDeclaration(statement)) {
if (statement.moduleSpecifier !== undefined && this._options & 4 /* ExportFrom */)
this._result.push(statement);
}
else if (node_1.isModuleDeclaration(statement)) {
this._findImportsInModule(statement);
}
}
}
_findImportsInModule(declaration) {
if (declaration.body === undefined)
return;
if (declaration.body.kind === ts.SyntaxKind.ModuleDeclaration)
return this._findImportsInModule(declaration.body);
this._findImports(declaration.body.statements);
}
_findNestedImports() {
const isJavaScriptFile = this._ignoreFileName || (this._sourceFile.flags & ts.NodeFlags.JavaScriptFile) !== 0;
let re;
let includeJsDoc;
if ((this._options & 56 /* AllNestedImports */) === 16 /* Require */) {
if (!isJavaScriptFile)
return; // don't look for 'require' in TS files
re = /\brequire\s*[</(]/g;
includeJsDoc = false;
}
else if (this._options & 16 /* Require */ && isJavaScriptFile) {
re = /\b(?:import|require)\s*[</(]/g;
includeJsDoc = (this._options & 32 /* ImportType */) !== 0;
}
else {
re = /\bimport\s*[</(]/g;
includeJsDoc = isJavaScriptFile && (this._options & 32 /* ImportType */) !== 0;
}
for (let match = re.exec(this._sourceFile.text); match !== null; match = re.exec(this._sourceFile.text)) {
const token = getTokenAtPositionWorker(this._sourceFile, match.index, this._sourceFile,
// only look for ImportTypeNode within JSDoc in JS files
match[0][0] === 'i' && includeJsDoc);
if (token.kind === ts.SyntaxKind.ImportKeyword) {
if (token.end - 'import'.length !== match.index)
continue;
switch (token.parent.kind) {
case ts.SyntaxKind.ImportType:
this._result.push(token.parent);
break;
case ts.SyntaxKind.CallExpression:
if (token.parent.arguments.length > 1)
this._result.push(token.parent);
}
}
else if (token.kind === ts.SyntaxKind.Identifier &&
token.end - 'require'.length === match.index &&
token.parent.kind === ts.SyntaxKind.CallExpression &&
token.parent.expression === token &&
token.parent.arguments.length === 1) {
this._result.push(token.parent);
}
}
}
}
/**
* Ambient context means the statement itself has the `declare` keyword
* or is inside a `declare namespace`, `delcare module` or `declare global`.
*/
function isStatementInAmbientContext(node) {
while (node.flags & ts.NodeFlags.NestedNamespace)
node = node.parent;
return hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword) || isAmbientModuleBlock(node.parent);
}
exports.isStatementInAmbientContext = isStatementInAmbientContext;
/** Includes `declare namespace`, `declare module` and `declare global` and namespace nested in one of the aforementioned. */
function isAmbientModuleBlock(node) {
while (node.kind === ts.SyntaxKind.ModuleBlock) {
do
node = node.parent;
while (node.flags & ts.NodeFlags.NestedNamespace);
if (hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword))
return true;
node = node.parent;
}
return false;
}
exports.isAmbientModuleBlock = isAmbientModuleBlock;
function getIIFE(func) {
let node = func.parent;
while (node.kind === ts.SyntaxKind.ParenthesizedExpression)
node = node.parent;
return node_1.isCallExpression(node) && func.end <= node.expression.end ? node : undefined;
}
exports.getIIFE = getIIFE;
function isStrictCompilerOptionEnabled(options, option) {
return (options.strict ? options[option] !== false : options[option] === true) &&
(option !== 'strictPropertyInitialization' || isStrictCompilerOptionEnabled(options, 'strictNullChecks'));
}
exports.isStrictCompilerOptionEnabled = isStrictCompilerOptionEnabled;
// https://github.com/ajafff/tslint-consistent-codestyle/issues/85
/**
* Checks if a given compiler option is enabled.
* It handles dependencies of options, e.g. `declaration` is implicitly enabled by `composite` or `strictNullChecks` is enabled by `strict`.
* However, it does not check dependencies that are already checked and reported as errors, e.g. `checkJs` without `allowJs`.
* This function only handles boolean flags.
*/
function isCompilerOptionEnabled(options, option) {
switch (option) {
case 'stripInternal':
case 'declarationMap':
case 'emitDeclarationOnly':
return options[option] === true && isCompilerOptionEnabled(options, 'declaration');
case 'declaration':
return options.declaration || isCompilerOptionEnabled(options, 'composite');
case 'incremental':
return options.incremental === undefined ? isCompilerOptionEnabled(options, 'composite') : options.incremental;
case 'skipDefaultLibCheck':
return options.skipDefaultLibCheck || isCompilerOptionEnabled(options, 'skipLibCheck');
case 'suppressImplicitAnyIndexErrors':
return options.suppressImplicitAnyIndexErrors === true && isCompilerOptionEnabled(options, 'noImplicitAny');
case 'allowSyntheticDefaultImports':
return options.allowSyntheticDefaultImports !== undefined
? options.allowSyntheticDefaultImports
: isCompilerOptionEnabled(options, 'esModuleInterop') || options.module === ts.ModuleKind.System;
case 'noUncheckedIndexedAccess':
return options.noUncheckedIndexedAccess === true && isCompilerOptionEnabled(options, 'strictNullChecks');
case 'allowJs':
return options.allowJs === undefined ? isCompilerOptionEnabled(options, 'checkJs') : options.allowJs;
case 'noImplicitAny':
case 'noImplicitThis':
case 'strictNullChecks':
case 'strictFunctionTypes':
case 'strictPropertyInitialization':
case 'alwaysStrict':
case 'strictBindCallApply':
return isStrictCompilerOptionEnabled(options, option);
}
return options[option] === true;
}
exports.isCompilerOptionEnabled = isCompilerOptionEnabled;
/**
* Has nothing to do with `isAmbientModuleBlock`.
*
* @returns `true` if it's a global augmentation or has a string name.
*/
function isAmbientModule(node) {
return node.name.kind === ts.SyntaxKind.StringLiteral || (node.flags & ts.NodeFlags.GlobalAugmentation) !== 0;
}
exports.isAmbientModule = isAmbientModule;
/**
* @deprecated use `getTsCheckDirective` instead since `// @ts-nocheck` is no longer restricted to JS files.
* @returns the last `// @ts-check` or `// @ts-nocheck` directive in the given file.
*/
function getCheckJsDirective(source) {
return getTsCheckDirective(source);
}
exports.getCheckJsDirective = getCheckJsDirective;
/** @returns the last `// @ts-check` or `// @ts-nocheck` directive in the given file. */
function getTsCheckDirective(source) {
let directive;
// needs to work around a shebang issue until https://github.com/Microsoft/TypeScript/issues/28477 is resolved
ts.forEachLeadingCommentRange(source, (ts.getShebang(source) || '').length, (pos, end, kind) => {
if (kind === ts.SyntaxKind.SingleLineCommentTrivia) {
const text = source.slice(pos, end);
const match = /^\/{2,3}\s*@ts-(no)?check(?:\s|$)/i.exec(text);
if (match !== null)
directive = { pos, end, enabled: match[1] === undefined };
}
});
return directive;
}
exports.getTsCheckDirective = getTsCheckDirective;
function isConstAssertion(node) {
return node_1.isTypeReferenceNode(node.type) &&
node.type.typeName.kind === ts.SyntaxKind.Identifier &&
node.type.typeName.escapedText === 'const';
}
exports.isConstAssertion = isConstAssertion;
/** Detects whether an expression is affected by an enclosing 'as const' assertion and therefore treated literally. */
function isInConstContext(node) {
let current = node;
while (true) {
const parent = current.parent;
outer: switch (parent.kind) {
case ts.SyntaxKind.TypeAssertionExpression:
case ts.SyntaxKind.AsExpression:
return isConstAssertion(parent);
case ts.SyntaxKind.PrefixUnaryExpression:
if (current.kind !== ts.SyntaxKind.NumericLiteral)
return false;
switch (parent.operator) {
case ts.SyntaxKind.PlusToken:
case ts.SyntaxKind.MinusToken:
current = parent;
break outer;
default:
return false;
}
case ts.SyntaxKind.PropertyAssignment:
if (parent.initializer !== current)
return false;
current = parent.parent;
break;
case ts.SyntaxKind.ShorthandPropertyAssignment:
current = parent.parent;
break;
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.ArrayLiteralExpression:
case ts.SyntaxKind.ObjectLiteralExpression:
case ts.SyntaxKind.TemplateExpression:
current = parent;
break;
default:
return false;
}
}
}
exports.isInConstContext = isInConstContext;
/** Returns true for `Object.defineProperty(o, 'prop', {value, writable: false})` and `Object.defineProperty(o, 'prop', {get: () => 1})`*/
function isReadonlyAssignmentDeclaration(node, checker) {
if (!isBindableObjectDefinePropertyCall(node))
return false;
const descriptorType = checker.getTypeAtLocation(node.arguments[2]);
if (descriptorType.getProperty('value') === undefined)
return descriptorType.getProperty('set') === undefined;
const writableProp = descriptorType.getProperty('writable');
if (writableProp === undefined)
return false;
const writableType = writableProp.valueDeclaration !== undefined && node_1.isPropertyAssignment(writableProp.valueDeclaration)
? checker.getTypeAtLocation(writableProp.valueDeclaration.initializer)
: checker.getTypeOfSymbolAtLocation(writableProp, node.arguments[2]);
return type_1.isBooleanLiteralType(writableType, false);
}
exports.isReadonlyAssignmentDeclaration = isReadonlyAssignmentDeclaration;
/** Determines whether a call to `Object.defineProperty` is statically analyzable. */
function isBindableObjectDefinePropertyCall(node) {
return node.arguments.length === 3 &&
node_1.isEntityNameExpression(node.arguments[0]) &&
node_1.isNumericOrStringLikeLiteral(node.arguments[1]) &&
node_1.isPropertyAccessExpression(node.expression) &&
node.expression.name.escapedText === 'defineProperty' &&
node_1.isIdentifier(node.expression.expression) &&
node.expression.expression.escapedText === 'Object';
}
exports.isBindableObjectDefinePropertyCall = isBindableObjectDefinePropertyCall;
function isWellKnownSymbolLiterally(node) {
return ts.isPropertyAccessExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.escapedText === 'Symbol';
}
exports.isWellKnownSymbolLiterally = isWellKnownSymbolLiterally;
/** @deprecated typescript 4.3 removed the concept of literal well known symbols. Use `getPropertyNameFromType` instead. */
function getPropertyNameOfWellKnownSymbol(node) {
return {
displayName: `[Symbol.${node.name.text}]`,
symbolName: ('__@' + node.name.text),
};
}
exports.getPropertyNameOfWellKnownSymbol = getPropertyNameOfWellKnownSymbol;
const isTsBefore43 = (([major, minor]) => major < '4' || major === '4' && minor < '3')(ts.versionMajorMinor.split('.'));
function getLateBoundPropertyNames(node, checker) {
const result = {
known: true,
names: [],
};
node = unwrapParentheses(node);
if (isTsBefore43 && isWellKnownSymbolLiterally(node)) {
result.names.push(getPropertyNameOfWellKnownSymbol(node)); // wotan-disable-line no-unstable-api-use
}
else {
const type = checker.getTypeAtLocation(node);
for (const key of type_1.unionTypeParts(checker.getBaseConstraintOfType(type) || type)) {
const propertyName = type_1.getPropertyNameFromType(key);
if (propertyName) {
result.names.push(propertyName);
}
else {
result.known = false;
}
}
}
return result;
}
exports.getLateBoundPropertyNames = getLateBoundPropertyNames;
function getLateBoundPropertyNamesOfPropertyName(node, checker) {
const staticName = getPropertyName(node);
return staticName !== undefined
? { known: true, names: [{ displayName: staticName, symbolName: ts.escapeLeadingUnderscores(staticName) }] }
: node.kind === ts.SyntaxKind.PrivateIdentifier
? { known: true, names: [{ displayName: node.text, symbolName: checker.getSymbolAtLocation(node).escapedName }] }
: getLateBoundPropertyNames(node.expression, checker);
}
exports.getLateBoundPropertyNamesOfPropertyName = getLateBoundPropertyNamesOfPropertyName;
/** Most declarations demand there to be only one statically known name, e.g. class members with computed name. */
function getSingleLateBoundPropertyNameOfPropertyName(node, checker) {
const staticName = getPropertyName(node);
if (staticName !== undefined)
return { displayName: staticName, symbolName: ts.escapeLeadingUnderscores(staticName) };
if (node.kind === ts.SyntaxKind.PrivateIdentifier)
return { displayName: node.text, symbolName: checker.getSymbolAtLocation(node).escapedName };
const { expression } = node;
return isTsBefore43 && isWellKnownSymbolLiterally(expression)
? getPropertyNameOfWellKnownSymbol(expression) // wotan-disable-line no-unstable-api-use
: type_1.getPropertyNameFromType(checker.getTypeAtLocation(expression));
}
exports.getSingleLateBoundPropertyNameOfPropertyName = getSingleLateBoundPropertyNameOfPropertyName;
function unwrapParentheses(node) {
while (node.kind === ts.SyntaxKind.ParenthesizedExpression)
node = node.expression;
return node;
}
exports.unwrapParentheses = unwrapParentheses;
function formatPseudoBigInt(v) {
return `${v.negative ? '-' : ''}${v.base10Value}n`;
}
exports.formatPseudoBigInt = formatPseudoBigInt;
/**
* Determines whether the given `SwitchStatement`'s `case` clauses cover every possible value of the switched expression.
* The logic is the same as TypeScript's control flow analysis.
* This does **not** check whether all `case` clauses do a certain action like assign a variable or return a value.
* This function ignores the `default` clause if present.
*/
function hasExhaustiveCaseClauses(node, checker) {
const caseClauses = node.caseBlock.clauses.filter(node_1.isCaseClause);
if (caseClauses.length === 0)
return false;
const typeParts = type_1.unionTypeParts(checker.getTypeAtLocation(node.expression));
if (typeParts.length > caseClauses.length)
return false;
const types = new Set(typeParts.map(getPrimitiveLiteralFromType));
if (types.has(undefined))
return false;
const seen = new Set();
for (const clause of caseClauses) {
const expressionType = checker.getTypeAtLocation(clause.expression);
if (exports.isTypeFlagSet(expressionType, ts.TypeFlags.Never))
continue; // additional case clause with 'never' is always allowed
const type = getPrimitiveLiteralFromType(expressionType);
if (types.has(type)) {
seen.add(type);
}
else if (type !== 'null' && type !== 'undefined') { // additional case clauses with 'null' and 'undefined' are always allowed
return false;
}
}
return types.size === seen.size;
}
exports.hasExhaustiveCaseClauses = hasExhaustiveCaseClauses;
function getPrimitiveLiteralFromType(t) {
if (exports.isTypeFlagSet(t, ts.TypeFlags.Null))
return 'null';
if (exports.isTypeFlagSet(t, ts.TypeFlags.Undefined))
return 'undefined';
if (exports.isTypeFlagSet(t, ts.TypeFlags.NumberLiteral))
return `${exports.isTypeFlagSet(t, ts.TypeFlags.EnumLiteral) ? 'enum:' : ''}${t.value}`;
if (exports.isTypeFlagSet(t, ts.TypeFlags.StringLiteral))
return `${exports.isTypeFlagSet(t, ts.TypeFlags.EnumLiteral) ? 'enum:' : ''}string:${t.value}`;
if (exports.isTypeFlagSet(t, ts.TypeFlags.BigIntLiteral))
return formatPseudoBigInt(t.value);
if (_3_2_1.isUniqueESSymbolType(t))
return t.escapedName;
if (type_1.isBooleanLiteralType(t, true))
return 'true';
if (type_1.isBooleanLiteralType(t, false))
return 'false';
}
function getBaseOfClassLikeExpression(node) {
var _a;
if (((_a = node.heritageClauses) === null || _a === void 0 ? void 0 : _a[0].token) === ts.SyntaxKind.ExtendsKeyword)
return node.heritageClauses[0].types[0];
}
exports.getBaseOfClassLikeExpression = getBaseOfClassLikeExpression;
//# sourceMappingURL=util.js.map