import { Console } from 'console'; import * as ts from 'typescript'; import { is } from 'uvu/assert'; import { Complexity } from './Complexity'; import { Counter } from './Counter'; import { DefinitionRange } from './DefinitionRange'; import { metaDescriptor, methodDescriptor, packageDescriptor, parameterDescriptor, termDescriptor, typeDescriptor, typeParameterDescriptor, } from './Descriptor'; import { Input } from './Input'; import * as lsif from './lsif'; import { FullRange, Moniker, Position, Range as LsifRange, ResultSet } from './lsif-data/lsif'; import { LsifSymbol } from './LsifSymbol'; import { Packages } from './Packages'; import { ParentChildRelationships } from './ParentChildRelationships'; import { Range } from './Range'; import { keywordStrings, keywordToStringMap, operatorTokenToStringMap } from './ToStringMaps'; type Descriptor = lsif.lib.codeintel.lsiftyped.Descriptor; export class FileIndexer { private localCounter = new Counter(); private propertyCounters: Map = new Map(); private localSymbolTable: Map = new Map(); private parentChildRelationships: ParentChildRelationships[] = Array(); // Since a file is a module, we don't want to start randomly in the parentChildRelationships array. // We want to aggregate parents from the "file"/module-level. private parentChildRelationshipsModuleLevel: ParentChildRelationships[] = Array(); public currentComponentHeirarchy: Complexity[] = new Array(); private currentComponentHeirarchyPositions: number[] = Array(); private indentLevel = 0; constructor( public readonly checker: ts.TypeChecker, public readonly input: Input, public readonly document: lsif.lib.codeintel.lsiftyped.Document, public readonly globalSymbolTable: Map, public readonly packages: Packages, public readonly sourceFile: ts.SourceFile, public readonly writeIndex: (index: any) => void, public readonly lsifCounter: Counter, public readonly definitions: Map = new Map(), public readonly references: Map, public readonly languageService: ts.LanguageService, public readonly dev: boolean, public readonly underTest: boolean = false ) {} public index(): void { this.visit(this.sourceFile); this.emitDocumentSymbolResults(); } private indent(): string { return '\t'.repeat(this.indentLevel); } private emitDocumentSymbolResults() { for (let parentChildRelationshipModuleLevel of this.parentChildRelationshipsModuleLevel) { if (parentChildRelationshipModuleLevel.children.length == 0) continue; this.writeIndex(parentChildRelationshipModuleLevel.getEmittable(this.lsifCounter.next())); } } private pushComponentToHeirarchy(id: number, isSourceFile: boolean = false) { this.indentLevel += 1; if (!isSourceFile) { let parentChildRelationship = new ParentChildRelationships(id, this.parentChildRelationships.length == 0); this.parentChildRelationships.push(parentChildRelationship); } this.currentComponentHeirarchy.push(new Complexity(id)); this.currentComponentHeirarchyPositions.push(0); this.currentComponentHeirarchyPositions = this.currentComponentHeirarchyPositions.map((element) => element - 1); } private popComponentFromHeirarchy(node: ts.Node) { this.indentLevel -= 1; let finalElementPosition = this.currentComponentHeirarchyPositions.pop(); if (this.underTest) return; let currentComplexityNode = this.currentComponentHeirarchy.pop(); if (this.dev) console.log('currentComplexityNode', currentComplexityNode); if (currentComplexityNode) { this.writeIndex(currentComplexityNode.getEmittable(this.lsifCounter.next())); } let child = this.parentChildRelationships.pop(); if (child === undefined) return; if (this.parentChildRelationships.length == 0) { this.parentChildRelationshipsModuleLevel.push(child); return; } this.parentChildRelationships[this.parentChildRelationships.length - 1].children.push(child); this.currentComponentHeirarchyPositions = this.currentComponentHeirarchyPositions.map((element) => element + 1); } private visit(node: ts.Node): void { if (this.dev) console.log(`${this.indent()}• visit [${node.pos}:${node.end}]`); if (!this.continueWalk(node)) return; if (this.dev) console.log('visit...'); ts.forEachChild(node, (node) => { if (this.dev) console.log(`${this.indent()}• visit.child [${node.pos}:${node.end}]`); this.visit(node); }); } private continueWalk(node: ts.Node): boolean { if (this.dev) console.log('continueWalk', node.kind); if (ts.isSourceFile(node)) return this.visitSourceFile(node); if (ts.isImportDeclaration(node)) return this.visitImportDeclaration(node); if (ts.isNamedImports(node)) return this.visitNamedImports(node); if (ts.isNamespaceImport(node)) return this.visitNamespaceImport(node); if (ts.isAsteriskToken(node)) return this.visitAsteriskToken(node); if (keywordToStringMap.has(node.kind)) return this.visitKeyword(node.kind); if (operatorTokenToStringMap.has(node.kind)) return this.visitOperator(node.kind); if (ts.isNumericLiteral(node) || ts.isBigIntLiteral(node)) return this.visitNumericLiteral(node); if (ts.isStringLiteral(node)) return this.visitStringLiteral(node); if (ts.isBlock(node)) return this.visitBlock(node); if (ts.isClassStaticBlockDeclaration(node)) return this.visitClassStaticBlockDeclaration(node); if (ts.isParameter(node)) return this.visitParameter(node); if (ts.isCallExpression(node)) return this.visitCallExpression(node); if (ts.isIdentifier(node)) return this.visitIdentifier(node); if (ts.isBinaryExpression(node)) return this.visitBinaryExpression(node); if (ts.isIfStatement(node)) return this.visitIfStatement(node); if (node.kind === ts.SyntaxKind.SyntaxList) return this.visitSyntaxList((node)); if (ts.isExpressionStatement(node)) return this.visitExpressionStatement(node); if (ts.isClassDeclaration(node)) return this.visitClassDeclaration(node); if (ts.isMethodDeclaration(node)) return this.visitMethodDeclaration(node); if (ts.isFunctionDeclaration(node)) return this.visitFunctionDeclaration(node); if (ts.isFunctionExpression(node)) return this.visitFunctionExpression(node); if (ts.isPropertyDeclaration(node)) return this.visitPropertyDeclaration(node); if (ts.isArrayTypeNode(node)) return this.visitArrayTypeNode(node); if (ts.isReturnStatement(node)) return this.visitReturnStatement(node); if (ts.isPropertyAccessExpression(node)) return this.visitPropertyAccessExpression(node); if (ts.isTypeReferenceNode(node)) return this.visitTypeReferenceNode(node); if (ts.isVariableStatement(node)) return this.visitVariableStatement(node); if (ts.isVariableDeclarationList(node)) return this.visitVariableDeclarationList(node); if (ts.isVariableDeclaration(node)) return this.visitVariableDeclaration(node); if (ts.isArrowFunction(node)) return this.visitArrowFunction(node); if (ts.isConstructorDeclaration(node)) return this.visitConstructorDeclaration(node); if (ts.isInterfaceDeclaration(node)) return this.visitInterfaceDeclaration(node); if (ts.isMethodSignature(node)) return this.visitMethodSignature(node); if (ts.isTypeParameterDeclaration(node)) return this.visitTypeParameterDeclaration(node); if (ts.isHeritageClause(node)) return this.visitHeritageClause(node); if (ts.isExpressionWithTypeArguments(node)) return this.visitExpressionWithTypeArguments(node); if (ts.isNewExpression(node)) return this.visitNewExpression(node); if (ts.isTypeAliasDeclaration(node)) return this.visitTypeAliasDeclaration(node); if (ts.isTypeLiteralNode(node)) return this.visitTypeLiteralNode(node); if (ts.isPropertySignature(node)) return this.visitPropertySignature(node); if (ts.isComputedPropertyName(node)) return this.visitComputedPropertyName(node); if (ts.isExportAssignment(node)) return this.visitExportAssignment(node); if (ts.isModuleDeclaration(node)) return this.visitModuleDeclaration(node); if (ts.isModuleBlock(node)) return this.visitModuleBlock(node); if (ts.isSetAccessorDeclaration(node)) return this.visitSetAccesssorDeclaration(node); if (ts.isGetAccessorDeclaration(node)) return this.visitGetAccessorDeclaration(node); if (ts.isSetAccessor(node)) return this.visitSetAccesssor(node); if (ts.isConstructSignatureDeclaration(node)) return this.visitConstructSignatureDeclaration(node); if (ts.isFunctionTypeNode(node)) return this.visitFunctionTypeNode(node); if (ts.isForStatement(node)) return this.visitForStatement(node); if (ts.isExportDeclaration(node)) return this.visitExportDeclaration(node); if (ts.isPrefixUnaryExpression(node)) return this.visitPrefixUnaryExpression(node); if (ts.isBreakStatement(node)) return this.visitBreakStatement(node); if (ts.isContinueStatement(node)) return this.visitContinueStatement(node); if (ts.isParenthesizedExpression(node)) return this.visitParenthesizedExpression(node); if (ts.isUnionTypeNode(node)) return this.visitUnionTypeNode(node); if (ts.isTypeQueryNode(node)) return this.visitTypeQueryNode(node); if (ts.isArrayLiteralExpression(node)) return this.visitArrayLiteralExpression(node); if (ts.isObjectBindingPattern(node)) return this.visitObjectBindingPattern(node); if (ts.isBindingElement(node)) return this.visitBindingElement(node); if (ts.isObjectLiteralExpression(node)) return this.visitObjectLiteralExpression(node); if (ts.isShorthandPropertyAssignment(node)) return this.visitShorthandPropertyAssignment(node); if (ts.isPropertyAssignment(node)) return this.visitPropertyAssignment(node); if (ts.isImportEqualsDeclaration(node)) return this.visitImportEqualsDeclaration(node); if (ts.isQualifiedName(node)) return this.visitQualifiedName(node); if (ts.isPrivateIdentifier(node)) return this.visitPrivateIdentifier(node); if (ts.isCallSignatureDeclaration(node)) return this.visitCallSignatureDeclaration(node); if (ts.isAwaitExpression(node)) return this.visitAwaitExpression(node); if (ts.isPostfixUnaryExpression(node)) return this.visitPostfixUnaryExpression(node); if (ts.isJsxText(node)) return this.visitJsxText(node); if (ts.isRegularExpressionLiteral(node)) return this.visitRegularExpressionLiteral(node); if (ts.isNoSubstitutionTemplateLiteral(node)) return this.visitNoSubstitutionTemplateLiteral(node); if (ts.isTemplateHead(node)) return this.visitTemplateHead(node); if (ts.isTemplateMiddle(node)) return this.visitTemplateMiddle(node); if (ts.isTemplateTail(node)) return this.visitTemplateTail(node); if (ts.isDotDotDotToken(node)) return this.visitDotDotDotToken(node); if (ts.isPlusToken(node)) return this.visitPlusToken(node); if (ts.isMinusToken(node)) return this.visitMinusToken(node); if (ts.isDecorator(node)) return this.visitDecorator(node); if (ts.isIndexSignatureDeclaration(node)) return this.visitIndexSignatureDeclaration(node); if (ts.isTypePredicateNode(node)) return this.visitTypePredicateNode(node); if (ts.isConstructorTypeNode(node)) return this.visitConstructorTypeNode(node); if (ts.isTupleTypeNode(node)) return this.visitTupleTypeNode(node); if (ts.isOptionalTypeNode(node)) return this.visitOptionalTypeNode(node); if (ts.isRestTypeNode(node)) return this.visitRestTypeNode(node); if (ts.isIntersectionTypeNode(node)) return this.visitIntersectionTypeNode(node); if (ts.isConditionalTypeNode(node)) return this.visitConditionalTypeNode(node); if (ts.isInferTypeNode(node)) return this.visitInferTypeNode(node); if (ts.isParenthesizedTypeNode(node)) return this.visitParenthesizedTypeNode(node); if (ts.isThisTypeNode(node)) return this.visitThisTypeNode(node); if (ts.isTypeOperatorNode(node)) return this.visitTypeOperatorNode(node); if (ts.isIndexedAccessTypeNode(node)) return this.visitIndexedAccessTypeNode(node); if (ts.isMappedTypeNode(node)) return this.visitMappedTypeNode(node); if (ts.isLiteralTypeNode(node)) return this.visitLiteralTypeNode(node); if (ts.isImportTypeNode(node)) return this.visitImportTypeNode(node); if (ts.isTemplateLiteralTypeSpan(node)) return this.visitTemplateLiteralTypeSpan(node); if (ts.isTemplateLiteralTypeNode(node)) return this.visitTemplateLiteralTypeNode(node); if (ts.isArrayBindingPattern(node)) return this.visitArrayBindingPattern(node); if (ts.isElementAccessExpression(node)) return this.visitElementAccessExpression(node); if (ts.isTaggedTemplateExpression(node)) return this.visitTaggedTemplateExpression(node); if (ts.isTypeAssertionExpression(node)) return this.visitTypeAssertionExpression(node); if (ts.isDeleteExpression(node)) return this.visitDeleteExpression(node); if (ts.isTypeOfExpression(node)) return this.visitTypeOfExpression(node); if (ts.isVoidExpression(node)) return this.visitVoidExpression(node); if (ts.isConditionalExpression(node)) return this.visitConditionalExpression(node); if (ts.isTemplateExpression(node)) return this.visitTemplateExpression(node); if (ts.isYieldExpression(node)) return this.visitYieldExpression(node); if (ts.isSpreadElement(node)) return this.visitSpreadElement(node); if (ts.isClassExpression(node)) return this.visitClassExpression(node); if (ts.isOmittedExpression(node)) return this.visitOmittedExpression(node); if (ts.isAsExpression(node)) return this.visitAsExpression(node); if (ts.isNonNullExpression(node)) return this.visitNonNullExpression(node); if (ts.isMetaProperty(node)) return this.visitMetaProperty(node); if (ts.isSyntheticExpression(node)) return this.visitSyntheticExpression(node); if (ts.isPartiallyEmittedExpression(node)) return this.visitPartiallyEmittedExpression(node); if (ts.isCommaListExpression(node)) return this.visitCommaListExpression(node); if (ts.isTemplateSpan(node)) return this.visitTemplateSpan(node); if (ts.isSemicolonClassElement(node)) return this.visitSemicolonClassElement(node); if (ts.isEmptyStatement(node)) return this.visitEmptyStatement(node); if (ts.isDoStatement(node)) return this.visitDoStatement(node); if (ts.isWhileStatement(node)) return this.visitWhileStatement(node); if (ts.isForInStatement(node)) return this.visitForInForOfStatement(node, 'in'); if (ts.isForOfStatement(node)) return this.visitForInForOfStatement(node, 'of'); if (ts.isWithStatement(node)) return this.visitWithStatement(node); if (ts.isSwitchStatement(node)) return this.visitSwitchStatement(node); if (ts.isLabeledStatement(node)) return this.visitLabeledStatement(node); if (ts.isThrowStatement(node)) return this.visitThrowStatement(node); if (ts.isTryStatement(node)) return this.visitTryStatement(node); if (ts.isDebuggerStatement(node)) return this.visitDebuggerStatement(node); if (ts.isEnumDeclaration(node)) return this.visitEnumDeclaration(node); if (ts.isCaseBlock(node)) return this.visitCaseBlock(node); if (ts.isNamespaceExportDeclaration(node)) return this.visitNamespaceExportDeclaration(node); if (ts.isImportClause(node)) return this.visitImportClause(node); if (ts.isImportTypeAssertionContainer(node)) return this.visitImportTypeAssertionContainer(node); if (ts.isAssertClause(node)) return this.visitAssertClause(node); if (ts.isAssertEntry(node)) return this.visitAssertEntry(node); if (ts.isNamespaceExport(node)) return this.visitNamespaceExport(node); if (ts.isImportSpecifier(node)) return this.visitImportSpecifier(node); if (ts.isNamedExports(node)) return this.visitNamedExports(node); if (ts.isExportSpecifier(node)) return this.visitExportSpecifier(node); if (ts.isMissingDeclaration(node)) return this.visitMissingDeclaration(node); if (ts.isNotEmittedStatement(node)) return this.visitNotEmittedStatement(node); if (ts.isExternalModuleReference(node)) return this.visitExternalModuleReference(node); if (ts.isJsxElement(node)) return this.visitJsxElement(node); if (ts.isJsxSelfClosingElement(node)) return this.visitJsxSelfClosingElement(node); if (ts.isJsxOpeningElement(node)) return this.visitJsxOpeningElement(node); if (ts.isJsxClosingElement(node)) return this.visitJsxClosingElement(node); if (ts.isJsxFragment(node)) return this.visitJsxFragment(node); if (ts.isJsxOpeningFragment(node)) return this.visitJsxOpeningFragment(node); if (ts.isJsxClosingFragment(node)) return this.visitJsxClosingFragment(node); if (ts.isJsxAttribute(node)) return this.visitJsxAttribute(node); if (ts.isJsxAttributes(node)) return this.visitJsxAttributes(node); if (ts.isJsxSpreadAttribute(node)) return this.visitJsxSpreadAttribute(node); if (ts.isJsxExpression(node)) return this.visitJsxExpression(node); if (ts.isCaseClause(node)) return this.visitCaseClause(node); if (ts.isDefaultClause(node)) return this.visitDefaultClause(node); if (ts.isCatchClause(node)) return this.visitCatchClause(node); if (ts.isSpreadAssignment(node)) return this.visitSpreadAssignment(node); if (ts.isEnumMember(node)) return this.visitEnumMember(node); if (ts.isUnparsedPrepend(node)) return this.visitUnparsedPrepend(node); if (ts.isBundle(node)) return this.visitBundle(node); if (ts.isUnparsedSource(node)) return this.visitUnparsedSource(node); if (ts.isJSDocTypeExpression(node)) return this.visitJSDocTypeExpression(node); if (ts.isJSDocNameReference(node)) return this.visitJSDocNameReference(node); if (ts.isJSDocMemberName(node)) return this.visitJSDocMemberName(node); if (ts.isJSDocLink(node)) return this.visitJSDocLink(node); if (ts.isJSDocLinkCode(node)) return this.visitJSDocLinkCode(node); if (ts.isJSDocLinkPlain(node)) return this.visitJSDocLinkPlain(node); if (ts.isJSDocAllType(node)) return this.visitJSDocAllType(node); if (ts.isJSDocUnknownType(node)) return this.visitJSDocUnknownType(node); if (ts.isJSDocNullableType(node)) return this.visitJSDocNullableType(node); if (ts.isJSDocNonNullableType(node)) return this.visitJSDocNonNullableType(node); if (ts.isJSDocOptionalType(node)) return this.visitJSDocOptionalType(node); if (ts.isJSDocFunctionType(node)) return this.visitJSDocFunctionType(node); if (ts.isJSDocVariadicType(node)) return this.visitJSDocVariadicType(node); if (ts.isJSDocNamepathType(node)) return this.visitJSDocNamepathType(node); if (ts.isJSDoc(node)) return this.visitJSDoc(node); if (ts.isJSDocTypeLiteral(node)) return this.visitJSDocTypeLiteral(node); if (ts.isJSDocSignature(node)) return this.visitJSDocSignature(node); if (ts.isJSDocAugmentsTag(node)) return this.visitJSDocAugmentsTag(node); if (ts.isJSDocAuthorTag(node)) return this.visitJSDocAuthorTag(node); if (ts.isJSDocClassTag(node)) return this.visitJSDocClassTag(node); if (ts.isJSDocCallbackTag(node)) return this.visitJSDocCallbackTag(node); if (ts.isJSDocPublicTag(node)) return this.visitJSDocPublicTag(node); if (ts.isJSDocPrivateTag(node)) return this.visitJSDocPrivateTag(node); if (ts.isJSDocProtectedTag(node)) return this.visitJSDocProtectedTag(node); if (ts.isJSDocReadonlyTag(node)) return this.visitJSDocReadonlyTag(node); if (ts.isJSDocOverrideTag(node)) return this.visitJSDocOverrideTag(node); if (ts.isJSDocDeprecatedTag(node)) return this.visitJSDocDeprecatedTag(node); if (ts.isJSDocSeeTag(node)) return this.visitJSDocSeeTag(node); if (ts.isJSDocEnumTag(node)) return this.visitJSDocEnumTag(node); if (ts.isJSDocParameterTag(node)) return this.visitJSDocParameterTag(node); if (ts.isJSDocReturnTag(node)) return this.visitJSDocReturnTag(node); if (ts.isJSDocThisTag(node)) return this.visitJSDocThisTag(node); if (ts.isJSDocTypeTag(node)) return this.visitJSDocTypeTag(node); if (ts.isJSDocTemplateTag(node)) return this.visitJSDocTemplateTag(node); if (ts.isJSDocTypedefTag(node)) return this.visitJSDocTypedefTag(node); if (ts.isJSDocUnknownTag(node)) return this.visitJSDocUnknownTag(node); if (ts.isJSDocPropertyTag(node)) return this.visitJSDocPropertyTag(node); if (ts.isJSDocImplementsTag(node)) return this.visitJSDocImplementsTag(node); return true; } private visitSourceFile(node: ts.SourceFile) { if (this.dev) console.log(`**FILE** -->${node.fileName}`); if (this.dev) console.log(`${this.indent()}• visitSourceFile [${node.pos}:${node.end}]`); this.pushComponentToHeirarchy(this.lsifCounter.get()); this.visitBlockStatements(node.statements); return false; } private visitImportDeclaration(node: ts.ImportDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitImportDeclaration [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['import']); if (node.importClause) { this.continueWalk(node.importClause); this.addOperatorsToAllHalstead(['from']); } this.continueWalk(node.moduleSpecifier); if (node.assertClause) { this.continueWalk(node.assertClause); } if (this.dev) console.log(`${this.indent()}• visitImportDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitNamespaceImport(node: ts.NamespaceImport): boolean { if (this.dev) console.log(`${this.indent()}• visitNamespaceImport [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['*', 'as']); this.continueWalk(node.name); if (this.dev) console.log(`${this.indent()}• visitNamespaceImport [${node.pos}:${node.end}] <>`); return false; } private visitNamedImports(node: ts.NamedImports): boolean { if (this.dev) console.log(`${this.indent()}• visitNamedImports [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements); if (this.dev) console.log(`${this.indent()}• visitNamedImports [${node.pos}:${node.end}] <>`); return false; } private visitAsteriskToken(node: ts.AsteriskToken): boolean { if (this.dev) console.log(`${this.indent()}• visitAsteriskToken [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['*']); if (this.dev) console.log(`${this.indent()}• visitAsteriskToken [${node.pos}:${node.end}] <>`); return false; } private visitKeyword(kind: ts.SyntaxKind): boolean { if (this.dev) console.log(`${this.indent()}• visitKeyword`); let keywordString = keywordToStringMap.get(kind); if (keywordString) { this.addOperatorsToAllHalstead([keywordString]); } if (this.dev) console.log(`${this.indent()}• visitKeyword <>`); return false; } private visitOperator(kind: ts.SyntaxKind): boolean { if (this.dev) console.log(`${this.indent()}• visitOperator`, kind); let operatorString = operatorTokenToStringMap.get(kind); if (operatorString) { this.addOperatorsToAllHalstead([operatorString]); } if (this.dev) console.log(`${this.indent()}• visitOperator <>`); return false; } private visitNumericLiteral(node: ts.NumericLiteral | ts.BigIntLiteral): boolean { if (this.dev) console.log(`${this.indent()}• visitNumericLiteral [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([node.text]); if (this.dev) console.log(`${this.indent()}• visitNumericLiteral [${node.pos}:${node.end}] <>`); return false; } private visitStringLiteral(node: ts.StringLiteral) { if (this.dev) console.log(`${this.indent()}• visitStringLiteral [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([`"${node.text}"`]); if (this.dev) console.log(`${this.indent()}• visitStringLiteral [${node.pos}:${node.end}] <>`); return false; } private visitBlock(node: ts.Block): boolean { if (this.dev) console.log(`${this.indent()}• visitBlock [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['{}']); node.statements.forEach((child) => { if (this.dev) console.log(`${this.indent()}• visitBlock [${node.pos}:${node.end}], node.child`, child); this.continueWalk(child); }); if (this.dev) console.log(`${this.indent()}• visitBlock [${node.pos}:${node.end}] <>`); return false; } private visitClassStaticBlockDeclaration(node: ts.ClassStaticBlockDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitClassStaticBlockDeclaration [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['static']); this.continueWalk(node.body); return false; } private visitParameter(node: ts.ParameterDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitParameter [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); if (node.dotDotDotToken) { this.addOperatorsToAllHalstead(['...']); } if (node.name) this.continueWalk(node.name); if (node.questionToken) { this.addOperatorsToAllHalstead(['?']); } if (node.type) { this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); } if (node.initializer) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.initializer); } if (this.dev) console.log(`${this.indent()}• visitParameter [${node.pos}:${node.end}] <>`); return false; } private visitArrayTypeNode(node: ts.ArrayTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitArrayTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.elementType); this.addOperatorsToAllHalstead(['[]']); if (this.dev) console.log(`${this.indent()}• visitArrayTypeNode [${node.pos}:${node.end}] <>`); return false; } private visitCallExpression(node: ts.CallExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitCallExpression [${node.pos}:${node.end}]`); this.continueWalk(node.expression); this.visitNodeArray(node.arguments, '()'); if (this.dev) console.log(`${this.indent()}• visitCallExpression [${node.pos}:${node.end}] <>`); return false; } private visitBinaryExpression(node: ts.BinaryExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitBinaryExpression [${node.pos}:${node.end}]`); this.continueWalk(node.left); this.continueWalk(node.operatorToken); this.continueWalk(node.right); if (this.dev) console.log(`${this.indent()}• visitBinaryExpression [${node.pos}:${node.end}] <>`); return false; } private visitIdentifier(node: ts.Identifier) { if (this.dev) console.log(`${this.indent()}• visitIdentifier [${node.pos}:${node.end}]`); let identifier = node.escapedText.toString(); if (this.dev) console.log(`${this.indent()}• visitIdentifier [${node.pos}:${node.end}]`, identifier); if (keywordStrings.indexOf(identifier) > -1) { this.addOperatorsToAllHalstead([identifier]); return false; } this.addOperandsToAllHalstead([identifier]); if (this.dev) console.log(`${this.indent()}• visitIdentifier [${node.pos}:${node.end}] <>`); return false; } private visitIfStatement(node: ts.IfStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitIfStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['if', '()']); this.continueWalk(node.expression); this.continueWalk(node.thenStatement); if (node.elseStatement) { this.addOperatorsToAllHalstead(['else']); this.continueWalk(node.elseStatement); } if (this.dev) console.log(`${this.indent()}• visitIfStatement [${node.pos}:${node.end}] <>`); return false; } private visitForStatement(node: ts.ForStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitForStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['for', '()']); if (node.initializer) this.continueWalk(node.initializer); this.addOperatorsToAllHalstead([';']); if (node.condition) this.continueWalk(node.condition); this.addOperatorsToAllHalstead([';']); if (node.incrementor) this.continueWalk(node.incrementor); this.continueWalk(node.statement); if (this.dev) console.log(`${this.indent()}• visitForStatement [${node.pos}:${node.end}] <>`); return false; } private visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitPrefixUnaryExpression [${node.pos}:${node.end}]`); this.visitOperator(node.operator); this.continueWalk(node.operand); if (this.dev) console.log(`${this.indent()}• visitPrefixUnaryExpression [${node.pos}:${node.end}] <>`); return false; } private visitPostfixUnaryExpression(node: ts.PostfixUnaryExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitPostfixUnaryExpression [${node.pos}:${node.end}]`); this.continueWalk(node.operand); this.visitOperator(node.operator); if (this.dev) console.log(`${this.indent()}• visitPostfixUnaryExpression [${node.pos}:${node.end}] <>`); return false; } private visitSyntaxList(node: ts.SyntaxList): boolean { if (this.dev) console.log(`${this.indent()}• visitSyntaxList [${node.pos}:${node.end}]`); node.getChildren().forEach((child) => { this.continueWalk(child); }); if (this.dev) console.log(`${this.indent()}• visitSyntaxList [${node.pos}:${node.end}] <>`); return false; } private visitExpressionStatement(node: ts.ExpressionStatement): boolean { if (this.dev) console.log( `${this.indent()}• visitExpressionStatement [${node.pos}:${node.end}], node.expression`, node.expression ); this.continueWalk(node.expression); if (this.dev) console.log(`${this.indent()}• visitExpressionStatement [${node.pos}:${node.end}] <>`); return false; } private visitReturnStatement(node: ts.ReturnStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitReturnStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['return']); if (node.expression) return this.continueWalk(node.expression); if (this.dev) console.log(`${this.indent()}• visitReturnStatement [${node.pos}:${node.end}] <>`); return false; } private visitPropertyAccessExpression(node: ts.PropertyAccessExpression): boolean { if (this.dev) console.log( `${this.indent()}• visitPropertyAccessExpression - ${node.expression}.${node.name.escapedText} - [${ node.pos }:${node.end}]`, node ); this.continueWalk(node.expression); if (node.questionDotToken) this.addOperatorsToAllHalstead(['?']); this.addOperatorsToAllHalstead(['.']); this.continueWalk(node.name); if (this.dev) console.log(`${this.indent()}• visitPropertyAccessExpression [${node.pos}:${node.end}] <>`); return false; } private visitComputedPropertyName(node: ts.ComputedPropertyName): boolean { if (this.dev) console.log(`${this.indent()}• visitComputedPropertyName [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['[]']); this.continueWalk(node.expression); return false; } private visitTypeParameterDeclaration(node: ts.TypeParameterDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeParameterDeclaration [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); if (node.constraint) { this.addOperatorsToAllHalstead(['[]']); this.continueWalk(node.name); this.addOperatorsToAllHalstead(['in']); this.continueWalk(node.constraint); return false; } if (node.default) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.default); } if (node.expression) throw new Error('************************ URGENT & NEW, node.expression'); this.continueWalk(node.name); if (this.dev) console.log(`${this.indent()}• visitTypeParameterDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitTypeReferenceNode(node: ts.TypeReferenceNode) { if (this.dev) console.log(`${this.indent()}• visitTypeReferenceNode [${node.pos}:${node.end}]`); this.continueWalk(node.typeName); if (node.typeArguments) { this.addOperatorsToAllHalstead(['<>']); this.visitNodeArray(node.typeArguments, '', ',', false); } if (this.dev) console.log(`${this.indent()}• visitTypeReferenceNode [${node.pos}:${node.end}] <>`); return false; } private visitVariableStatement(node: ts.VariableStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitVariableStatement [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); this.continueWalk(node.declarationList); if (this.dev) console.log(`${this.indent()}• visitVariableStatement [${node.pos}:${node.end}] <>`); return false; } private visitVariableDeclarationList(node: ts.VariableDeclarationList): boolean { if (this.dev) console.log(`${this.indent()}• visitVariableDeclarationList [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); // TODO - investigate https://github.com/source-field/sourcefield-lsif-typescript/pull/1#discussion_r977227847 if (node.flags == ts.NodeFlags.None || node.flags & ts.NodeFlags.None) this.addOperatorsToAllHalstead(['var']); if (node.flags == ts.NodeFlags.Let || node.flags & ts.NodeFlags.Let) this.addOperatorsToAllHalstead(['let']); if (node.flags == ts.NodeFlags.Const || node.flags & ts.NodeFlags.Const) this.addOperatorsToAllHalstead(['const']); const maxLoop = node.declarations.length - 1; node.declarations.forEach((declaration, i) => { if (this.dev) console.log(`${this.indent()}• visitVariableDeclarationList visiting declaration`, declaration.kind); this.continueWalk(declaration); if (i != maxLoop) this.addOperatorsToAllHalstead([',']); }); if (this.dev) console.log(`${this.indent()}• visitVariableDeclarationList [${node.pos}:${node.end}] <>`); return false; } private visitVariableDeclaration(node: ts.VariableDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitVariableDeclaration [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); this.continueWalk(node.name); if (node.type) { this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); } // TODO - find one that has an initializer...unsure of the OOO if (node.initializer) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.initializer); } if (node.exclamationToken) this.continueWalk(node.exclamationToken); if (this.dev) console.log(`${this.indent()}• visitVariableDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitArrowFunction(node: ts.ArrowFunction): boolean { if (this.dev) console.log(`${this.indent()}• visitArrowFunction [${node.pos}:${node.end}]`, node); this.visitNodeArray(node.parameters, '()', ','); // TODO - solve why some arrow functions have this and others do not if (node.equalsGreaterThanToken.pos != node.equalsGreaterThanToken.end) this.addOperatorsToAllHalstead(['=>']); this.continueWalk(node.body); if (this.dev) console.log(`${this.indent()}• visitArrowFunction [${node.pos}:${node.end}] <>`); return false; } private visitAwaitExpression(node: ts.AwaitExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitAwaitExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['await']); this.continueWalk(node.expression); return false; } private visitHeritageClause(node: ts.HeritageClause): boolean { if (this.dev) console.log(`${this.indent()}• visitHeritageClause [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); this.visitNodeArray(node.types, '', ',', false); if (this.dev) console.log(`${this.indent()}• visitHeritageClause [${node.pos}:${node.end}] <>`); return false; } private visitExpressionWithTypeArguments(node: ts.ExpressionWithTypeArguments): boolean { if (this.dev) console.log(`${this.indent()}• visitExpressionWithTypeArguments [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); this.continueWalk(node.expression); if (node.typeArguments) this.visitNodeArray(node.typeArguments, '<>'); if (this.dev) console.log(`${this.indent()}• visitExpressionWithTypeArguments [${node.pos}:${node.end}] <>`); return false; } private visitNewExpression(node: ts.NewExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitNewExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['new']); this.continueWalk(node.expression); if (node.arguments) this.visitNodeArray(node.arguments, '()'); return false; } private visitTypeLiteralNode(node: ts.TypeLiteralNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeLiteralNode [${node.pos}:${node.end}]`); this.visitNodeArray(node.members, '{}', ','); return false; } private visitPropertySignature(node: ts.PropertySignature): boolean { if (this.dev) console.log(`${this.indent()}• visitPropertySignature [${node.pos}:${node.end}]`); this.continueWalk(node.name); if (node.questionToken) this.addOperatorsToAllHalstead(['?']); if (node.type) { this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); } return false; } private visitExportAssignment(node: ts.ExportAssignment): boolean { if (this.dev) console.log(`${this.indent()}• visitExportAssignment [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['export']); if (node.isExportEquals) { this.addOperatorsToAllHalstead(['=']); } else { this.addOperatorsToAllHalstead(['default']); } this.continueWalk(node.expression); return false; } private visitModuleBlock(node: ts.ModuleBlock): boolean { if (this.dev) console.log(`${this.indent()}• visitModuleBlock [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['{}']); this.visitBlockStatements(node.statements); return false; } private visitBlockStatements(statements: ts.NodeArray): boolean { if (this.dev) console.log(`${this.indent()}• visitBlockStatements`, statements); for (const statement of statements) { if (statement.flags & ts.NodeFlags.ThisNodeHasError) continue; if (this.dev) console.log('** visitSourceFile.statement', statement.pos, statement.end); this.continueWalk(statement); } return false; } private visitSetAccesssor(node: ts.SetAccessorDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitSetAccessor [${node.pos}:${node.end}]`); return false; } private visitFunctionTypeNode(node: ts.FunctionTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitFunctionTypeNode [${node.pos}:${node.end}]`); this.visitNodeArray(node.parameters, '()'); this.addOperatorsToAllHalstead(['=>']); this.continueWalk(node.type); return false; } private visitBreakStatement(node: ts.BreakStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitBreakStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['break']); return false; } private visitContinueStatement(node: ts.ContinueStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitContinueStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['continue']); return false; } private visitParenthesizedExpression(node: ts.ParenthesizedExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitParenthesizedExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['()']); this.continueWalk(node.expression); return false; } private visitUnionTypeNode(node: ts.UnionTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitUnionTypeNode [${node.pos}:${node.end}]`); this.visitNodeArray(node.types, '', '|', false); return false; } private visitTypeQueryNode(node: ts.TypeQueryNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeQueryNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['typeof']); this.continueWalk(node.exprName); return false; } private visitArrayLiteralExpression(node: ts.ArrayLiteralExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitArrayLiteralExpression [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements, '[]'); return false; } private visitObjectBindingPattern(node: ts.ObjectBindingPattern): boolean { if (this.dev) console.log(`${this.indent()}• visitObjectBindingPattern [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements, '{}'); return false; } private visitBindingElement(node: ts.BindingElement): boolean { if (this.dev) console.log(`${this.indent()}• visitBindingElement [${node.pos}:${node.end}]`); if (node.propertyName) { this.continueWalk(node.propertyName); this.addOperatorsToAllHalstead([':']); } this.continueWalk(node.name); return false; } private visitObjectLiteralExpression(node: ts.ObjectLiteralExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitObjectLiteralExpression [${node.pos}:${node.end}]`); this.visitNodeArray(node.properties, '{}'); return false; } private visitShorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment): boolean { if (this.dev) console.log(`${this.indent()}• visitShorthandPropertyAssignment [${node.pos}:${node.end}]`); this.continueWalk(node.name); return false; } private visitPropertyAssignment(node: ts.PropertyAssignment): boolean { if (this.dev) console.log(`${this.indent()}• visitPropertyAssignment [${node.pos}:${node.end}]`); this.continueWalk(node.name); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.initializer); return false; } private visitQualifiedName(node: ts.QualifiedName): boolean { if (this.dev) console.log(`${this.indent()}• visitQualifiedName [${node.pos}:${node.end}]`); this.continueWalk(node.left); this.addOperatorsToAllHalstead(['.']); this.continueWalk(node.right); return false; } private visitPrivateIdentifier(node: ts.PrivateIdentifier): boolean { if (this.dev) console.log(`${this.indent()}• visitPrivateIdentifier [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([node.escapedText.toString()]); return false; } private visitJsxText(node: ts.JsxText): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxText [${node.pos}:${node.end}]`); let text = node.text.replace(/(\r\n\s*|\n\s*|\r\s*)/gm, ''); if (text == '') return false; this.addOperandsToAllHalstead([`"${text}"`]); return false; } private visitRegularExpressionLiteral(node: ts.RegularExpressionLiteral): boolean { if (this.dev) console.log(`${this.indent()}• visitRegularExpressionLiteral [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([node.text]); return false; } private visitNoSubstitutionTemplateLiteral(node: ts.NoSubstitutionTemplateLiteral): boolean { if (this.dev) console.log(`${this.indent()}• visitNoSubstitutionTemplateLiteral [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([`"${node.text}"`]); return false; } private visitTemplateHead(node: ts.TemplateHead): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateHead [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([`"${node.text}"`]); return false; } private visitTemplateMiddle(node: ts.TemplateMiddle): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateMiddle [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([`"${node.text}"`]); return false; } private visitTemplateTail(node: ts.TemplateTail): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateTail [${node.pos}:${node.end}]`); this.addOperandsToAllHalstead([`"${node.text}"`]); return false; } private visitDotDotDotToken(node: ts.DotDotDotToken): boolean { if (this.dev) console.log(`${this.indent()}• visitDotDotDotToken [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['...']); return false; } private visitPlusToken(node: ts.PlusToken): boolean { if (this.dev) console.log(`${this.indent()}• visitPlusToken [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['+']); return false; } private visitMinusToken(node: ts.MinusToken): boolean { if (this.dev) console.log(`${this.indent()}• visitMinusToken [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['-']); return false; } private visitDecorator(node: ts.Decorator): boolean { if (this.dev) console.log(`${this.indent()}• visitDecorator [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['@']); this.continueWalk(node.expression); return false; } private visitIndexSignatureDeclaration(node: ts.IndexSignatureDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitIndexSignatureDeclaration [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['[]']); this.visitNodeArray(node.parameters, '', ',', false); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); return false; } private visitTypePredicateNode(node: ts.TypePredicateNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTypePredicateNode [${node.pos}:${node.end}]`); this.continueWalk(node.parameterName); if (node.type) { this.addOperatorsToAllHalstead(['is']); this.continueWalk(node.type); } return false; } private visitConstructorTypeNode(node: ts.ConstructorTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitConstructorTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['new']); this.visitNodeArray(node.parameters, '()'); this.addOperatorsToAllHalstead(['=>']); this.continueWalk(node.type); return false; } private visitTupleTypeNode(node: ts.TupleTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTupleTypeNode [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements, '[]'); return false; } private visitOptionalTypeNode(node: ts.OptionalTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitOptionalTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.type); this.addOperatorsToAllHalstead(['?']); return false; } private visitRestTypeNode(node: ts.RestTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitRestTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['...']); this.continueWalk(node.type); return false; } private visitIntersectionTypeNode(node: ts.IntersectionTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitIntersectionTypeNode [${node.pos}:${node.end}]`); this.visitNodeArray(node.types, '', '&', false); return false; } private visitConditionalTypeNode(node: ts.ConditionalTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitConditionalTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.checkType); this.addOperatorsToAllHalstead(['extends']); this.continueWalk(node.extendsType); this.addOperatorsToAllHalstead(['?']); this.continueWalk(node.trueType); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.falseType); return false; } private visitInferTypeNode(node: ts.InferTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitInferTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['infer']); this.continueWalk(node.typeParameter); return false; } private visitParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitParenthesizedTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['()']); this.continueWalk(node.type); return false; } private visitThisTypeNode(node: ts.ThisTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitThisTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['this']); return false; } private visitTypeOperatorNode(node: ts.TypeOperatorNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeOperatorNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead([keywordToStringMap.get(node.operator)!]); this.continueWalk(node.type); return false; } private visitIndexedAccessTypeNode(node: ts.IndexedAccessTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitIndexedAccessTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.objectType); this.addOperatorsToAllHalstead(['[]']); this.continueWalk(node.indexType); return false; } private visitMappedTypeNode(node: ts.MappedTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitMappedTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['{}']); this.continueWalk(node.typeParameter); if (node.type) { this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); } return false; } private visitLiteralTypeNode(node: ts.LiteralTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitLiteralTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.literal); return false; } private visitImportTypeNode(node: ts.ImportTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitImportTypeNode [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['typeof', 'import', '()']); this.continueWalk(node.argument); this.addOperatorsToAllHalstead(['.']); if (node.qualifier) this.continueWalk(node.qualifier); return false; } private visitTemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateLiteralTypeSpan [${node.pos}:${node.end}]`); this.continueWalk(node.type); this.continueWalk(node.literal); return false; } private visitTemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateLiteralTypeNode [${node.pos}:${node.end}]`); this.continueWalk(node.head); this.visitNodeArray(node.templateSpans, '', ',', false); return false; } private visitArrayBindingPattern(node: ts.ArrayBindingPattern): boolean { if (this.dev) console.log(`${this.indent()}• visitArrayBindingPattern [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements, '[]'); return false; } private visitElementAccessExpression(node: ts.ElementAccessExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitElementAccessExpression [${node.pos}:${node.end}]`); this.continueWalk(node.expression); this.addOperatorsToAllHalstead(['[]']); this.continueWalk(node.argumentExpression); return false; } private visitTaggedTemplateExpression(node: ts.TaggedTemplateExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitTaggedTemplateExpression [${node.pos}:${node.end}]`); this.continueWalk(node.tag); this.continueWalk(node.template); if (this.dev) console.log(`${this.indent()}• visitTaggedTemplateExpression <>`); return false; } private visitTypeAssertionExpression(node: ts.TypeAssertion): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeAssertionExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['<>']); this.continueWalk(node.type); this.continueWalk(node.expression); return false; } private visitDeleteExpression(node: ts.DeleteExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitDeleteExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['delete']); this.continueWalk(node.expression); return false; } private visitTypeOfExpression(node: ts.TypeOfExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeOfExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['typeof']); this.continueWalk(node.expression); return false; } private visitVoidExpression(node: ts.VoidExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitVoidExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['void']); this.continueWalk(node.expression); return false; } private visitConditionalExpression(node: ts.ConditionalExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitConditionalExpression [${node.pos}:${node.end}]`); this.continueWalk(node.condition); this.addOperatorsToAllHalstead(['?']); this.continueWalk(node.whenTrue); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.whenFalse); return false; } private visitTemplateExpression(node: ts.TemplateExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateExpression [${node.pos}:${node.end}]`); this.continueWalk(node.head); this.visitNodeArray(node.templateSpans, '', '', false, false); return false; } private visitYieldExpression(node: ts.YieldExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitYieldExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['yield']); if (node.asteriskToken) this.addOperatorsToAllHalstead(['*']); if (node.expression) this.continueWalk(node.expression); return false; } private visitSpreadElement(node: ts.SpreadElement): boolean { if (this.dev) console.log(`${this.indent()}• visitSpreadElement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['...']); this.continueWalk(node.expression); return false; } private visitClassExpression(node: ts.ClassExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitClassExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['class']); if (node.heritageClauses) { this.visitNodeArray(node.heritageClauses, '', ',', false); } if (node.members) { this.visitNodeArray(node.members, '{}', ',', true, false); } return false; } private visitOmittedExpression(node: ts.OmittedExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitOmittedExpression [${node.pos}:${node.end}]`); return false; } private visitAsExpression(node: ts.AsExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitAsExpression [${node.pos}:${node.end}]`); this.continueWalk(node.expression); this.addOperatorsToAllHalstead(['as']); this.continueWalk(node.type); return false; } private visitNonNullExpression(node: ts.NonNullExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitNonNullExpression [${node.pos}:${node.end}]`); this.continueWalk(node.expression); this.addOperatorsToAllHalstead(['!']); return false; } private visitMetaProperty(node: ts.MetaProperty): boolean { if (this.dev) console.log(`${this.indent()}• visitMetaProperty [${node.pos}:${node.end}]`); this.visitKeyword(node.keywordToken); this.addOperatorsToAllHalstead(['.']); this.continueWalk(node.name); return false; } private visitSyntheticExpression(node: ts.SyntheticExpression): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitSyntheticExpression [${node.pos}:${node.end}]`); return false; } private visitPartiallyEmittedExpression(node: ts.PartiallyEmittedExpression): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitPartiallyEmittedExpression [${node.pos}:${node.end}]`); return false; } private visitCommaListExpression(node: ts.CommaListExpression): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitCommaListExpression [${node.pos}:${node.end}]`); return false; } private visitTemplateSpan(node: ts.TemplateSpan): boolean { if (this.dev) console.log(`${this.indent()}• visitTemplateSpan [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['${}']); this.continueWalk(node.expression); this.continueWalk(node.literal); return false; } private visitSemicolonClassElement(node: ts.SemicolonClassElement): boolean { if (this.dev) console.log(`${this.indent()}• visitSemicolonClassElement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead([';']); return false; } private visitEmptyStatement(node: ts.EmptyStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitEmptyStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead([';']); return false; } private visitDoStatement(node: ts.DoStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitDoStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['do']); this.continueWalk(node.statement); this.addOperatorsToAllHalstead(['while', '()']); this.continueWalk(node.expression); return false; } private visitWhileStatement(node: ts.WhileStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitWhileStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['while', '()']); this.continueWalk(node.expression); this.continueWalk(node.statement); return false; } private visitForInForOfStatement(node: ts.ForInStatement | ts.ForOfStatement, inOf: string): boolean { if (this.dev) console.log(`${this.indent()}• visitForInStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['for', '()']); this.continueWalk(node.initializer); this.addOperatorsToAllHalstead([inOf]); this.continueWalk(node.expression); this.continueWalk(node.statement); return false; } private visitWithStatement(node: ts.WithStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitWithStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['with', '()']); this.continueWalk(node.expression); this.continueWalk(node.statement); return false; } private visitSwitchStatement(node: ts.SwitchStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitSwitchStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['switch', '()']); this.continueWalk(node.expression); this.continueWalk(node.caseBlock); return false; } private visitLabeledStatement(node: ts.LabeledStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitLabeledStatement [${node.pos}:${node.end}]`); this.continueWalk(node.label); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.statement); return false; } private visitThrowStatement(node: ts.ThrowStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitThrowStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['throw']); this.continueWalk(node.expression); return false; } private visitTryStatement(node: ts.TryStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitTryStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['try']); this.visitBlock(node.tryBlock); if (node.catchClause) this.continueWalk(node.catchClause); return false; } private visitDebuggerStatement(node: ts.DebuggerStatement): boolean { if (this.dev) console.log(`${this.indent()}• visitDebuggerStatement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['debugger']); return false; } private visitEnumDeclaration(node: ts.EnumDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitEnumDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['enum']); return false; } private visitCaseBlock(node: ts.CaseBlock): boolean { if (this.dev) console.log(`${this.indent()}• visitCaseBlock [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['{}']); this.visitNodeArray(node.clauses, '', '', false, false); return false; } private visitNamespaceExportDeclaration(node: ts.NamespaceExportDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitNamespaceExportDeclaration [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['export', 'as', 'namespace']); this.continueWalk(node.name); return false; } private visitImportClause(node: ts.ImportClause): boolean { if (this.dev) console.log(`${this.indent()}• visitImportClause [${node.pos}:${node.end}]`); if (node.isTypeOnly) this.addOperatorsToAllHalstead(['type']); if (node.name) this.continueWalk(node.name); if (node.namedBindings) this.continueWalk(node.namedBindings); return false; } private visitImportTypeAssertionContainer(node: ts.ImportTypeAssertionContainer): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitImportTypeAssertionContainer [${node.pos}:${node.end}]`); return false; } private visitAssertClause(node: ts.AssertClause): boolean { if (this.dev) console.log(`${this.indent()}• visitAssertClause [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['assert']); this.visitNodeArray(node.elements); return false; } private visitAssertEntry(node: ts.AssertEntry): boolean { if (this.dev) console.log(`${this.indent()}• visitAssertEntry [${node.pos}:${node.end}]`); this.continueWalk(node.name); this.addOperatorsToAllHalstead([':']); this.continueWalk(node.value); return false; } private visitNamespaceExport(node: ts.NamespaceExport): boolean { if (this.dev) console.log(`${this.indent()}• visitNamespaceExport [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['*', 'as']); this.continueWalk(node.name); return false; } private visitImportSpecifier(node: ts.ImportSpecifier): boolean { if (this.dev) console.log(`${this.indent()}• visitImportSpecifier [${node.pos}:${node.end}]`); if (node.isTypeOnly) this.addOperatorsToAllHalstead(['type']); if (node.propertyName) { this.continueWalk(node.propertyName); this.addOperatorsToAllHalstead(['as']); } this.continueWalk(node.name); return false; } private visitNamedExports(node: ts.NamedExports): boolean { if (this.dev) console.log(`${this.indent()}• visitNamedExports [${node.pos}:${node.end}]`); this.visitNodeArray(node.elements, '{}', ','); return false; } private visitExportSpecifier(node: ts.ExportSpecifier): boolean { if (this.dev) console.log(`${this.indent()}• visitExportSpecifier [${node.pos}:${node.end}]`); this.continueWalk(node.name); return false; } private visitMissingDeclaration(node: ts.MissingDeclaration): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitMissingDeclaration [${node.pos}:${node.end}]`); return false; } private visitNotEmittedStatement(node: ts.NotEmittedStatement): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitNotEmittedStatement [${node.pos}:${node.end}]`); return false; } private visitExternalModuleReference(node: ts.ExternalModuleReference): boolean { if (this.dev) console.log(`${this.indent()}• visitExternalModuleReference [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['require', '()']); this.continueWalk(node.expression); return false; } private visitJsxElementStartingNode(node: ts.JsxSelfClosingElement | ts.JsxOpeningElement): boolean { this.continueWalk(node.tagName); this.continueWalk(node.attributes); return false; } private visitJsxElement(node: ts.JsxElement): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxElement [${node.pos}:${node.end}]`); this.continueWalk(node.openingElement); this.visitNodeArray(node.children, '', '', false, false); this.continueWalk(node.closingElement); return false; } private visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxSelfClosingElement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['< />']); this.visitJsxElementStartingNode(node); return false; } private visitJsxOpeningElement(node: ts.JsxOpeningElement): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxOpeningElement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['<>']); this.visitJsxElementStartingNode(node); return false; } private visitJsxClosingElement(node: ts.JsxClosingElement): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxClosingElement [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['']); this.continueWalk(node.tagName); return false; } private visitJsxFragment(node: ts.JsxFragment): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxFragment [${node.pos}:${node.end}]`); this.continueWalk(node.openingFragment); this.visitNodeArray(node.children, '', '', false, false); this.continueWalk(node.closingFragment); return false; } private visitJsxOpeningFragment(node: ts.JsxOpeningFragment): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxOpeningFragment [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['<>']); return false; } private visitJsxClosingFragment(node: ts.JsxClosingFragment): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxClosingFragment [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['']); return false; } private visitJsxAttribute(node: ts.JsxAttribute): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxAttribute [${node.pos}:${node.end}]`); this.continueWalk(node.name); if (node.initializer) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.initializer); } return false; } private visitJsxAttributes(node: ts.JsxAttributes): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxAttributes [${node.pos}:${node.end}]`); this.visitNodeArray(node.properties, '', '', false, false); return false; } private visitJsxSpreadAttribute(node: ts.JsxSpreadAttribute): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxSpreadAttribute [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['...']); this.continueWalk(node.expression); return false; } private visitJsxExpression(node: ts.JsxExpression): boolean { if (this.dev) console.log(`${this.indent()}• visitJsxExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['{}']); if (node.dotDotDotToken) this.addOperatorsToAllHalstead(['...']); if (node.expression) this.continueWalk(node.expression); return false; } private visitCaseClause(node: ts.CaseClause): boolean { if (this.dev) console.log(`${this.indent()}• visitCaseClause [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['case']); this.continueWalk(node.expression); this.addOperatorsToAllHalstead([':']); this.visitBlockStatements(node.statements); return false; } private visitDefaultClause(node: ts.DefaultClause): boolean { if (this.dev) console.log(`${this.indent()}• visitDefaultClause [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['default', ':']); this.visitBlockStatements(node.statements); return false; } private visitCatchClause(node: ts.CatchClause): boolean { if (this.dev) console.log(`${this.indent()}• visitCatchClause [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['catch']); if (node.variableDeclaration) { this.addOperatorsToAllHalstead(['()']); this.continueWalk(node.variableDeclaration); } this.visitBlock(node.block); return false; } private visitSpreadAssignment(node: ts.SpreadAssignment): boolean { if (this.dev) console.log(`${this.indent()}• visitSpreadAssignment [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['...']); this.continueWalk(node.expression); return false; } private visitEnumMember(node: ts.EnumMember): boolean { if (this.dev) console.log(`${this.indent()}• visitEnumMember [${node.pos}:${node.end}]`); this.continueWalk(node.name); this.addOperatorsToAllHalstead(['=']); if (node.initializer) this.continueWalk(node.initializer); return false; } private visitUnparsedPrepend(node: ts.UnparsedPrepend): boolean { if (this.dev) console.log(`${this.indent()}• visitUnparsedPrepend [${node.pos}:${node.end}]`); console.log('** Parse problem!! **', node); return false; } private visitBundle(node: ts.Bundle): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitBundle [${node.pos}:${node.end}]`); return false; } private visitUnparsedSource(node: ts.UnparsedSource): boolean { if (this.dev) console.log(`${this.indent()}• visitUnparsedSource [${node.pos}:${node.end}]`); console.log('** Parse problem!! **', node); return false; } private visitJSDocTypeExpression(node: ts.JSDocTypeExpression): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocTypeExpression [${node.pos}:${node.end}]`); return false; } private visitJSDocNameReference(node: ts.JSDocNameReference): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocNameReference [${node.pos}:${node.end}]`); return false; } private visitJSDocMemberName(node: ts.JSDocMemberName): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocMemberName [${node.pos}:${node.end}]`); return false; } private visitJSDocLink(node: ts.JSDocLink): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocLink [${node.pos}:${node.end}]`); return false; } private visitJSDocLinkCode(node: ts.JSDocLinkCode): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocLinkCode [${node.pos}:${node.end}]`); return false; } private visitJSDocLinkPlain(node: ts.JSDocLinkPlain): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocLinkPlain [${node.pos}:${node.end}]`); return false; } private visitJSDocAllType(node: ts.JSDocAllType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocAllType [${node.pos}:${node.end}]`); return false; } private visitJSDocUnknownType(node: ts.JSDocUnknownType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocUnknownType [${node.pos}:${node.end}]`); return false; } private visitJSDocNullableType(node: ts.JSDocNullableType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocNullableType [${node.pos}:${node.end}]`); return false; } private visitJSDocNonNullableType(node: ts.JSDocNonNullableType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocNonNullableType [${node.pos}:${node.end}]`); return false; } private visitJSDocOptionalType(node: ts.JSDocOptionalType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocOptionalType [${node.pos}:${node.end}]`); return false; } private visitJSDocFunctionType(node: ts.JSDocFunctionType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocFunctionType [${node.pos}:${node.end}]`); return false; } private visitJSDocVariadicType(node: ts.JSDocVariadicType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocVariadicType [${node.pos}:${node.end}]`); return false; } private visitJSDocNamepathType(node: ts.JSDocNamepathType): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocNamepathType [${node.pos}:${node.end}]`); return false; } private visitJSDoc(node: ts.JSDoc): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDoc(node [${node.pos}:${node.end}]`); return false; } private visitJSDocTypeLiteral(node: ts.JSDocTypeLiteral): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocTypeLiteral [${node.pos}:${node.end}]`); return false; } private visitJSDocSignature(node: ts.JSDocSignature): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocSignature [${node.pos}:${node.end}]`); return false; } private visitJSDocAugmentsTag(node: ts.JSDocAugmentsTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocAugmentsTag [${node.pos}:${node.end}]`); return false; } private visitJSDocAuthorTag(node: ts.JSDocAuthorTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocAuthorTag [${node.pos}:${node.end}]`); return false; } private visitJSDocClassTag(node: ts.JSDocClassTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocClassTag [${node.pos}:${node.end}]`); return false; } private visitJSDocCallbackTag(node: ts.JSDocCallbackTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocCallbackTag [${node.pos}:${node.end}]`); return false; } private visitJSDocPublicTag(node: ts.JSDocPublicTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocPublicTag [${node.pos}:${node.end}]`); return false; } private visitJSDocPrivateTag(node: ts.JSDocPrivateTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocPrivateTag [${node.pos}:${node.end}]`); return false; } private visitJSDocProtectedTag(node: ts.JSDocProtectedTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocProtectedTag [${node.pos}:${node.end}]`); return false; } private visitJSDocReadonlyTag(node: ts.JSDocReadonlyTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocReadonlyTag [${node.pos}:${node.end}]`); return false; } private visitJSDocOverrideTag(node: ts.JSDocOverrideTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocOverrideTag [${node.pos}:${node.end}]`); return false; } private visitJSDocDeprecatedTag(node: ts.JSDocDeprecatedTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocDeprecatedTag [${node.pos}:${node.end}]`); return false; } private visitJSDocSeeTag(node: ts.JSDocSeeTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocSeeTag [${node.pos}:${node.end}]`); return false; } private visitJSDocEnumTag(node: ts.JSDocEnumTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocEnumTag [${node.pos}:${node.end}]`); return false; } private visitJSDocParameterTag(node: ts.JSDocParameterTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocParameterTag [${node.pos}:${node.end}]`); return false; } private visitJSDocReturnTag(node: ts.JSDocReturnTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocReturnTag [${node.pos}:${node.end}]`); return false; } private visitJSDocThisTag(node: ts.JSDocThisTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocThisTag [${node.pos}:${node.end}]`); return false; } private visitJSDocTypeTag(node: ts.JSDocTypeTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocTypeTag [${node.pos}:${node.end}]`); return false; } private visitJSDocTemplateTag(node: ts.JSDocTemplateTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocTemplateTag [${node.pos}:${node.end}]`); return false; } private visitJSDocTypedefTag(node: ts.JSDocTypedefTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocTypedefTag [${node.pos}:${node.end}]`); return false; } private visitJSDocUnknownTag(node: ts.JSDocUnknownTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocUnknownTag [${node.pos}:${node.end}]`); return false; } private visitJSDocPropertyTag(node: ts.JSDocPropertyTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocPropertyTag [${node.pos}:${node.end}]`); return false; } private visitJSDocImplementsTag(node: ts.JSDocImplementsTag): boolean { if (this.dev) console.log(`!!!!!!!!!${this.indent()}• visitJSDocImplementsTag [${node.pos}:${node.end}]`); return false; } // Declarations private visitModuleDeclaration(node: ts.ModuleDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitModuleDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['module']); if (this.dev) console.log(`${this.indent()}• visitModuleDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitImportEqualsDeclaration [${node.pos}:${node.end}]`); node.modifiers?.forEach((modifier) => this.continueWalk(modifier)); this.addOperatorsToAllHalstead(['import']); this.continueWalk(node.name); this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.moduleReference); return false; } private visitExportDeclaration(node: ts.ExportDeclaration): boolean { // TODO - should this live as a "component" if (this.dev) console.log(`${this.indent()}• visitExportDeclaration [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['export']); if (node.exportClause) this.continueWalk(node.exportClause); if (node.moduleSpecifier) { this.addOperatorsToAllHalstead(['from']); this.continueWalk(node.moduleSpecifier); } return false; } private visitClassDeclaration(node: ts.ClassDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}]`); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: COMPONENT PUSHED TO HEIRARCHY -->` ); // node.decorators?.forEach((decorator) => { // console.log(`${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: visiting decorators`); // this.continueWalk(decorator); // }); node.modifiers?.forEach((modifier) => { if (this.dev) console.log(`${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: visiting modifiers`); this.continueWalk(modifier); }); this.addOperatorsToAllHalstead(['class']); if (node.name) this.continueWalk(node.name); if ('typeParameters' in node && node.typeParameters) { if (this.dev) console.log( `${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: visiting typeParameters` ); this.visitNodeArray(node.typeParameters, '<>'); } if ('heritageClauses' in node && node.heritageClauses) { if (this.dev) console.log( `${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: visiting heritageClauses`, node.heritageClauses ); this.visitNodeArray(node.heritageClauses, '', ',', false); } if ('members' in node && node.members) { if (this.dev) console.log(`${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] :: visiting members`); this.visitNodeArray(node.members, '{}', ',', true, false); if (node.members.hasTrailingComma) this.addOperatorsToAllHalstead([',']); } this.popComponentFromHeirarchy(node); if (this.dev) console.log( `${this.indent()}• visitClassDeclaration [${node.pos}:${ node.end }] :: COMPONENT POPPED FROM HEIRARCHY <--` ); if (this.dev) console.log(`${this.indent()}• visitClassDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitInterfaceDeclaration(node: ts.InterfaceDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}]`); if (this.dev) console.log(`${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}]`, node.kind, node); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${ node.end }] :: COMPONENT PUSHED TO HEIRARCHY -->` ); // node.decorators?.forEach((decorator) => { // console.log(`${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] :: visiting decorators`); // this.continueWalk(decorator); // }); node.modifiers?.forEach((modifier) => { if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] :: visiting modifiers` ); this.continueWalk(modifier); }); this.addOperatorsToAllHalstead(['interface']); if (node.name) this.continueWalk(node.name); if ('heritageClauses' in node && node.heritageClauses) { if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] :: visiting heritageClauses`, node.heritageClauses ); this.visitNodeArray(node.heritageClauses, '', ',', false); } if ('typeParameters' in node && node.typeParameters) { if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] :: visiting typeParameters` ); this.visitNodeArray(node.typeParameters, '<>'); } if ('members' in node && node.members) { if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] :: visiting members` ); this.visitNodeArray(node.members, '{}', ',', true, false); if (node.members.hasTrailingComma) this.addOperatorsToAllHalstead([',']); } this.popComponentFromHeirarchy(node); if (this.dev) console.log( `${this.indent()}• visitInterfaceDeclaration [${node.pos}:${ node.end }] :: COMPONENT POPPED FROM HEIRARCHY <--` ); if (this.dev) console.log(`${this.indent()}• visitInterfaceDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitGetAccessorDeclaration(node: ts.GetAccessorDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitGetAccessorDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['get']); if (this.dev) console.log(`${this.indent()}• visitGetAccessorDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitSetAccesssorDeclaration(node: ts.SetAccessorDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitSetAccesssorDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['set']); if (this.dev) console.log(`${this.indent()}• visitSetAccesssorDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitConstructSignatureDeclaration(node: ts.ConstructSignatureDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitConstructSignatureDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['new']); if (this.dev) console.log(`${this.indent()}• visitConstructSignatureDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitCallSignatureDeclaration(node: ts.CallSignatureDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitCallSignatureDeclaration [${node.pos}:${node.end}]`); this.visitNodeArray(node.parameters, '()'); this.addOperatorsToAllHalstead([':']); if (node.type) this.continueWalk(node.type); if (this.dev) console.log(`${this.indent()}• visitCallSignatureDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}]`); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${ node.end }] :: COMPONENT PUSHED TO HEIRARCHY -->` ); // node.decorators?.forEach((decorator) => { // console.log(`${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}] :: visiting decorators`); // this.continueWalk(decorator); // }); node.modifiers?.forEach((modifier) => { if (this.dev) console.log( `${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}] :: visiting modifiers` ); this.continueWalk(modifier); }); this.addOperatorsToAllHalstead(['type']); if (node.name) this.continueWalk(node.name); if ('typeParameters' in node && node.typeParameters) { if (this.dev) console.log( `${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}] :: visiting typeParameters` ); this.visitNodeArray(node.typeParameters, '<>'); } this.addOperatorsToAllHalstead(['=']); if ('type' in node && node.type) { if (!ts.isTypeAliasDeclaration(node)) this.addOperatorsToAllHalstead([':']); if (this.dev) console.log(`${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}] :: visiting type`); this.continueWalk(node.type); } this.popComponentFromHeirarchy(node); if (this.dev) console.log( `${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${ node.end }] :: COMPONENT POPPED FROM HEIRARCHY <--` ); if (this.dev) console.log(`${this.indent()}• visitTypeAliasDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitMethodSignature(node: ts.MethodSignature): boolean { if (this.dev) console.log(`${this.indent()}• visitMethodSignature [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, []); if (this.dev) console.log(`${this.indent()}• visitMethodSignature [${node.pos}:${node.end}] <>`); return false; } private visitConstructorDeclaration(node: ts.ConstructorDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitConstructorDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['constructor']); if (this.dev) console.log(`${this.indent()}• visitConstructorDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitMethodDeclaration(node: ts.MethodDeclaration): boolean { if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}]`); if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}]`, node.kind, node); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitMethodDeclaration [${node.pos}:${ node.end }] :: COMPONENT PUSHED TO HEIRARCHY -->` ); // node.decorators?.forEach((decorator) => { // console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting decorators`); // this.continueWalk(decorator); // }); node.modifiers?.forEach((modifier) => { if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting modifiers`); this.continueWalk(modifier); }); if (node.name) this.continueWalk(node.name); if ('asteriskToken' in node && node.asteriskToken) this.addOperatorsToAllHalstead(['*']); if ('questionToken' in node && node.questionToken) this.addOperatorsToAllHalstead(['?']); if ('parameters' in node) { if (this.dev) console.log( `${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting parameters` ); this.visitNodeArray(node.parameters, '()'); } if ('type' in node && node.type) { if (!ts.isTypeAliasDeclaration(node)) this.addOperatorsToAllHalstead([':']); if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting type`); this.continueWalk(node.type); } if ('typeParameters' in node && node.typeParameters) { if (this.dev) console.log( `${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting typeParameters` ); this.visitNodeArray(node.typeParameters, '<>'); } if ('body' in node && node.body) { if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] :: visiting body`); this.continueWalk(node.body); } this.popComponentFromHeirarchy(node); if (this.dev) console.log( `${this.indent()}• visitMethodDeclaration [${node.pos}:${ node.end }] :: COMPONENT POPPED FROM HEIRARCHY <--` ); if (this.dev) console.log(`${this.indent()}• visitMethodDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitNodeArray( nodes: | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray | ts.NodeArray, enclosingOperator: string = '{}', separator: string = ',', includeEnclosingOperator: boolean = true, includeSeparator: boolean = true ): boolean { if (this.dev) console.log(`${this.indent()}• visitNodeArray`); if (includeEnclosingOperator) this.addOperatorsToAllHalstead([enclosingOperator]); const maxLoop = nodes.length - 1; nodes.forEach((node, i) => { if (this.dev) console.log(`${this.indent()}• visitNodeArray, maxLoop: ${maxLoop}`, node.kind); if ('token' in node) this.visitKeyword(node.token); this.continueWalk(node); if (i != maxLoop && includeSeparator) { if (this.dev) console.log(`${this.indent()}• visitNodeArray adding separator`, separator); this.addOperatorsToAllHalstead([separator]); } }); if (this.dev) console.log(`${this.indent()}• visitNodeArray <>`); return false; } private visitFunctionDeclaration(node: ts.FunctionDeclaration) { if (this.dev) console.log(`${this.indent()}• visitFunctionDeclaration [${node.pos}:${node.end}]`); this.visitDeclarationWithBody(node, ['function']); if (this.dev) console.log(`${this.indent()}• visitFunctionDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitFunctionExpression(node: ts.FunctionExpression) { if (this.dev) console.log(`${this.indent()}• visitFunctionExpression [${node.pos}:${node.end}]`); this.addOperatorsToAllHalstead(['function']); if (node.name) this.continueWalk(node.name); this.visitNodeArray(node.parameters, '()'); if (node.type) { this.addOperatorsToAllHalstead([':']); this.continueWalk(node.type); } this.continueWalk(node.body); if (this.dev) console.log(`${this.indent()}• visitFunctionExpression [${node.pos}:${node.end}] <>`); return false; } private visitPropertyDeclaration(node: ts.PropertyDeclaration) { if (this.dev) console.log(`${this.indent()}• visitPropertyDeclaration [${node.pos}:${node.end}]`); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitPropertyDeclaration [${node.pos}:${ node.end }] :: COMPONENT PUSHED TO HEIRARCHY -->` ); node.modifiers?.forEach((modifier) => { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting modifiers` ); this.continueWalk(modifier); }); if (node.name) this.continueWalk(node.name); if ('questionToken' in node && node.questionToken) this.addOperatorsToAllHalstead(['?']); if (node.type) { if (!ts.isTypeAliasDeclaration(node)) this.addOperatorsToAllHalstead([':']); if (this.dev) console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting type`); this.continueWalk(node.type); } if (node.initializer) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.initializer); } this.popComponentFromHeirarchy(node); if (this.dev) console.log(`${this.indent()}• visitPropertyDeclaration [${node.pos}:${node.end}] <>`); return false; } private visitDeclarationWithBody( node: | ts.ModuleDeclaration | ts.FunctionDeclaration | ts.MethodDeclaration | ts.MethodSignature | ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration | ts.ClassStaticBlockDeclaration | ts.FunctionExpression // | | ts.ClassDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration | ts.TypeParameterDeclaration | ts.PropertyDeclaration | ts.ConstructSignatureDeclaration | ts.VariableDeclaration // | | ts.TypeAliasDeclaration, operatorsToPush: string[] ) { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}]`, node.kind, operatorsToPush, node ); let lsifSymbol = this.lsifSymbol(node); if (this.underTest) { this.pushComponentToHeirarchy(this.lsifCounter.next()); } else { let id = this.emitDeclaration(node, lsifSymbol); this.pushComponentToHeirarchy(id); this.getAndStoreReferences(id, node); } if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${ node.end }] :: COMPONENT PUSHED TO HEIRARCHY -->` ); // node.decorators?.forEach((decorator) => { // console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting decorators`); // this.continueWalk(decorator); // }); node.modifiers?.forEach((modifier) => { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting modifiers` ); this.continueWalk(modifier); }); this.addOperatorsToAllHalstead(operatorsToPush); if (node.name) this.continueWalk(node.name); if ('asteriskToken' in node && node.asteriskToken) this.addOperatorsToAllHalstead(['*']); if ('questionToken' in node && node.questionToken) this.addOperatorsToAllHalstead(['?']); if ('heritageClauses' in node && node.heritageClauses) { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting heritageClauses`, node.heritageClauses ); this.visitNodeArray(node.heritageClauses, '', ',', false); } if ('typeParameters' in node && node.typeParameters) { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting typeParameters` ); this.visitNodeArray(node.typeParameters, '<>'); } if ('parameters' in node) { if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting parameters` ); this.visitNodeArray(node.parameters, '()'); } if ('initializer' in node && node.initializer) { this.addOperatorsToAllHalstead(['=']); this.continueWalk(node.initializer); } if ('type' in node && node.type) { if (!ts.isTypeAliasDeclaration(node)) this.addOperatorsToAllHalstead([':']); if (this.dev) console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting type`); this.continueWalk(node.type); } if ('body' in node && node.body) { if (this.dev) console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting body`); this.continueWalk(node.body); } if ('members' in node && node.members) { if (this.dev) console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] :: visiting members`); this.visitNodeArray(node.members, '{}', ',', true, true); if (node.members.hasTrailingComma) this.addOperatorsToAllHalstead([',']); } this.popComponentFromHeirarchy(node); if (this.dev) console.log( `${this.indent()}• visitDeclarationWithBody [${node.pos}:${ node.end }] :: COMPONENT POPPED FROM HEIRARCHY <--` ); if (this.dev) console.log(`${this.indent()}• visitDeclarationWithBody [${node.pos}:${node.end}] <>`); return false; } /** * Adds all supplied operands to every corresponding Halstead measurement array (.operands) * * @param {(string | number | bigint)[]} operands - Operands to add to measurement array */ addOperandsToAllHalstead(operands: (string | number | bigint)[]) { this.currentComponentHeirarchyPositions.forEach((position) => { this.currentComponentHeirarchy[this.currentComponentHeirarchy.length + position].halstead.operands.push( ...operands ); if (this.dev) console.log( this.currentComponentHeirarchy[this.currentComponentHeirarchy.length + position].halstead.operands ); }); } /** * Adds all supplied operators to every corresponding Halstead measurement array (.operators) * * @param {string[]} operators - Operators to add to measurement array */ addOperatorsToAllHalstead(operators: string[]) { this.currentComponentHeirarchyPositions.forEach((position) => { this.currentComponentHeirarchy[this.currentComponentHeirarchy.length + position].halstead.operators.push( ...operators ); if (this.dev) console.log( this.currentComponentHeirarchy[this.currentComponentHeirarchy.length + position].halstead.operators ); }); } private getDeclarationKind(declaration: ts.Node): number { if (ts.isModuleDeclaration(declaration)) return 2; if (ts.isNamespaceExportDeclaration(declaration)) return 3; if (ts.isClassDeclaration(declaration)) return 5; if ( ts.isMethodDeclaration(declaration) || ts.isConstructSignatureDeclaration(declaration) || ts.isGetAccessorDeclaration(declaration) || ts.isSetAccessorDeclaration(declaration) ) return 6; if (ts.isPropertyDeclaration(declaration)) return 7; if (ts.isConstructorDeclaration(declaration)) return 9; if (ts.isEnumDeclaration(declaration)) return 10; if (ts.isInterfaceDeclaration(declaration)) return 11; if (ts.isFunctionDeclaration(declaration)) return 12; if (ts.isVariableDeclaration(declaration)) return 13; if (ts.isTypeParameterDeclaration(declaration)) return 26; return 0; } private emitDeclaration( node: | ts.ModuleDeclaration | ts.ClassDeclaration | ts.EnumDeclaration | ts.FunctionDeclaration | ts.FunctionExpression | ts.ClassStaticBlockDeclaration | ts.InterfaceDeclaration | ts.TypeParameterDeclaration | ts.PropertyDeclaration | ts.MethodDeclaration | ts.MethodSignature | ts.ConstructSignatureDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration | ts.PropertyDeclaration | ts.ConstructorDeclaration | ts.VariableDeclaration | ts.TypeAliasDeclaration, lsifSymbol: LsifSymbol ): number { this.writeIndex(new ResultSet({ id: this.lsifCounter.next(), type: 'vertex', label: 'resultSet' })); this.writeIndex(new Moniker({ id: this.lsifCounter.next(), type: 'vertex', label: 'moniker' })); const id = this.lsifCounter.next(); const rangeLsif = Range.fromNode(node); const start = new Position({ line: rangeLsif.start.line, character: rangeLsif.start.character, }); const end = new Position({ line: rangeLsif.end.line, character: rangeLsif.end.character, }); let lsifRange = new LsifRange({ id, type: 'vertex', label: 'range', start, end, tag: new LsifRange.Tag({ text: lsifSymbol.value, kind: this.getDeclarationKind(node), fullRange: new FullRange({ start, end }), }), }); this.definitions.set(id, new DefinitionRange(this.sourceFile.fileName, rangeLsif)); this.writeIndex(lsifRange); return id; } private getAndStoreReferences( id: number, node: | ts.ModuleDeclaration | ts.ClassDeclaration | ts.ClassStaticBlockDeclaration | ts.ConstructorDeclaration | ts.ConstructSignatureDeclaration | ts.EnumDeclaration | ts.FunctionDeclaration | ts.FunctionExpression | ts.GetAccessorDeclaration | ts.InterfaceDeclaration | ts.MethodDeclaration | ts.MethodSignature | ts.PropertyDeclaration | ts.SetAccessorDeclaration | ts.TypeParameterDeclaration | ts.VariableDeclaration | ts.TypeAliasDeclaration ) { if (!node.name) return; const foundReferences = this.languageService.findReferences(this.sourceFile.fileName, node.name.getStart()); if (!this.references.has(id)) this.references.set(id, new Array()); if (foundReferences) { foundReferences.forEach((foundReference) => { let referencesRangeArray = foundReference.references.map((reference) => { return new DefinitionRange( reference.fileName, Range.fromTextSpan(this.sourceFile, reference.textSpan) ); }); this.references.get(id)!.push(...referencesRangeArray); }); } } private lsifSymbol(node: ts.Node): LsifSymbol { const fromCache: LsifSymbol | undefined = this.globalSymbolTable.get(node) || this.localSymbolTable.get(node); if (fromCache) { return fromCache; } if (ts.isBlock(node)) { return LsifSymbol.empty(); } if (ts.isSourceFile(node)) { const package_ = this.packages.symbol(node.fileName); if (!package_) { return this.cached(node, LsifSymbol.empty()); } return this.cached(node, package_); } if (ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node)) { const name = node.name.getText(); let counter = this.propertyCounters.get(name); if (!counter) { counter = new Counter(); this.propertyCounters.set(name, counter); } return this.cached( node, LsifSymbol.global( this.lsifSymbol(node.getSourceFile()), metaDescriptor(`${node.name.getText()}${counter.next()}`) ) ); } if (ts.isJsxAttribute(node)) { // NOTE(olafurpg): the logic below is a bit convoluted but I spent several // hours and failed to come up with a cleaner solution. JSX attributes // have custom typechecking rules, as documented here // https://www.typescriptlang.org/docs/handbook/jsx.html#type-checking The // only way to access the actual symbol we want to reference appears to go // through the JSX opening element, which is the grandparent of the JSX // attribute node. Through the signature of the opening element, we get // the permitted attributes by querying the type of the first parameter. const jsxElement = node.parent.parent; const props = this.checker.getResolvedSignature(jsxElement)?.getParameters()?.[0]; if (props) { try { const tpe = this.checker.getTypeOfSymbolAtLocation(props, node); const property = tpe.getProperty(node.name.text); for (const decl of property?.declarations || []) { return this.lsifSymbol(decl); } } catch { // TODO: https://github.com/sourcegraph/lsif-typescript/issues/34 // continue regardless of error, the TypeScript compiler tends to // trigger stack overflows in getTypeOfSymbolAtLocation and we // don't know why yet. } } } try { const owner = this.lsifSymbol(node.parent); if (owner.isEmpty() || owner.isLocal()) { return this.newLocalSymbol(node); } if (isAnonymousContainerOfSymbols(node)) { return this.cached(node, this.lsifSymbol(node.parent)); } if (ts.isImportSpecifier(node) || ts.isImportClause(node)) { const tpe = this.checker.getTypeAtLocation(node); for (const declaration of tpe.symbol?.declarations || []) { return this.lsifSymbol(declaration); } } const desc = this.descriptor(node); if (desc) { return this.cached(node, LsifSymbol.global(owner, desc)); } } catch (error) { console.log(error); } // Fallback case: generate a local symbol. It's not a bug when this case // happens. For example, we hit this case for block `{}` that are local // symbols, which are direct children of global symbols (toplevel // functions). return this.newLocalSymbol(node); } private newLocalSymbol(node: ts.Node): LsifSymbol { const symbol = LsifSymbol.local(this.localCounter.next()); this.localSymbolTable.set(node, symbol); return symbol; } private cached(node: ts.Node, symbol: LsifSymbol): LsifSymbol { this.globalSymbolTable.set(node, symbol); return symbol; } private descriptor(node: ts.Node): Descriptor | undefined { if (ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) { return typeDescriptor(node.name.getText()); } if (ts.isClassLike(node)) { const name = node.name?.getText(); if (name) { return typeDescriptor(name); } } if (ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) { const name = node.name?.getText(); if (name) { return methodDescriptor(name); } } if (ts.isConstructorDeclaration(node)) { return methodDescriptor(''); } if ( ts.isPropertyDeclaration(node) || ts.isPropertySignature(node) || ts.isEnumMember(node) || ts.isVariableDeclaration(node) ) { return termDescriptor(node.name.getText()); } if (ts.isAccessor(node)) { const prefix = ts.isGetAccessor(node) ? '' : ''; return methodDescriptor(prefix + node.name.getText()); } if (ts.isModuleDeclaration(node)) { return packageDescriptor(node.name.getText()); } if (ts.isParameter(node)) { return parameterDescriptor(node.name.getText()); } if (ts.isTypeParameterDeclaration(node)) { return typeParameterDescriptor(node.name.getText()); } return undefined; } } function isAnonymousContainerOfSymbols(node: ts.Node): boolean { return ( ts.isModuleBlock(node) || ts.isImportDeclaration(node) || (ts.isImportClause(node) && !node.name) || ts.isNamedImports(node) || ts.isVariableStatement(node) || ts.isVariableDeclarationList(node) ); }