UNPKG

@jqassistant/ts-lce

Version:

Tool to extract language concepts from a TypeScript codebase and export them to a JSON file.

239 lines (238 loc) 21.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AutoAccessorDeclarationProcessor = exports.PropertyProcessor = exports.MethodParameterProcessor = exports.MethodProcessor = void 0; const utils_1 = require("@typescript-eslint/utils"); const concept_1 = require("../concept"); const decorator_concept_1 = require("../concepts/decorator.concept"); const method_declaration_concept_1 = require("../concepts/method-declaration.concept"); const property_declaration_concept_1 = require("../concepts/property-declaration.concept"); const type_concept_1 = require("../concepts/type.concept"); const context_1 = require("../context"); const execution_condition_1 = require("../execution-condition"); const processor_1 = require("../processor"); const processor_utils_1 = require("../utils/processor.utils"); const expression_traverser_1 = require("../traversers/expression.traverser"); const method_traverser_1 = require("../traversers/method.traverser"); const property_traverser_1 = require("../traversers/property.traverser"); const dependency_resolution_processor_1 = require("./dependency-resolution.processor"); const type_utils_1 = require("./type.utils"); const code_coordinate_utils_1 = require("./code-coordinate.utils"); const accessor_declaration_concept_1 = require("../concepts/accessor-declaration.concept"); const context_keys_1 = require("../context.keys"); const function_traverser_1 = require("../traversers/function.traverser"); const type_annotation_concept_1 = require("../concepts/type-annotation.concept"); /** * Extracts concepts for methods of globally declared (abstract) classes and interfaces */ class MethodProcessor extends processor_1.Processor { executionCondition = new execution_condition_1.ExecutionCondition([utils_1.AST_NODE_TYPES.MethodDefinition, utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition, utils_1.AST_NODE_TYPES.TSMethodSignature], ({ localContexts }) => { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_CLASS_LIKE_MEMBERS); return !!flagContext && flagContext[1] == flagContext[0]; // e.g. Method(0) <- ClassBody(1) <- ClassDeclaration(2) (same for interface) }); preChildrenProcessing({ node, localContexts, globalContext, ...unusedProcessingContext }) { if ((node.type === utils_1.AST_NODE_TYPES.MethodDefinition || node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || node.type === utils_1.AST_NODE_TYPES.TSMethodSignature) && !node.computed) { const [methodName, jsPrivate] = processMemberName(node.key); dependency_resolution_processor_1.DependencyResolutionProcessor.addScopeContext(localContexts, context_1.FQN.id(methodName)); dependency_resolution_processor_1.DependencyResolutionProcessor.createDependencyIndex(localContexts); // TODO: secure against Indexed Access Types const functionType = (0, type_utils_1.parseMethodType)({ globalContext, localContexts, node, ...unusedProcessingContext, }, determineParentDeclarationNode(node, localContexts), node, methodName, jsPrivate); if (functionType) { localContexts.currentContexts.set(context_keys_1.CoreContextKeys.METHOD_TYPE, functionType); } if (node.type === utils_1.AST_NODE_TYPES.TSMethodSignature) { localContexts.currentContexts.set(context_keys_1.CoreContextKeys.PROCESS_PARAMETERS, 1); } else { // in the case of method definitions, the additional nested function expression node must be accounted for localContexts.currentContexts.set(context_keys_1.CoreContextKeys.PROCESS_PARAMETERS, 2); } } } postChildrenProcessing({ node, localContexts, globalContext }, childConcepts) { if ((node.type === utils_1.AST_NODE_TYPES.MethodDefinition || node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || node.type === utils_1.AST_NODE_TYPES.TSMethodSignature) && !node.computed) { // TODO: handle overloads const methodType = localContexts.currentContexts.get(context_keys_1.CoreContextKeys.METHOD_TYPE); const parentDeclNode = determineParentDeclarationNode(node, localContexts); if (methodType) { const [methodName, jsPrivate] = processMemberName(node.key); const visibility = jsPrivate ? "js_private" : (node.accessibility ?? "public"); const inClass = parentDeclNode.type === utils_1.AST_NODE_TYPES.ClassDeclaration || parentDeclNode.type === utils_1.AST_NODE_TYPES.ClassExpression; const fqn = dependency_resolution_processor_1.DependencyResolutionProcessor.constructScopeFQN(localContexts); dependency_resolution_processor_1.DependencyResolutionProcessor.registerDeclaration(localContexts, methodName, fqn, true); let methodConcept = new Map(); if (node.kind === "method") { // method if (methodType) { methodConcept = (0, concept_1.singleEntryConceptMap)(method_declaration_concept_1.LCEMethodDeclaration.conceptId, new method_declaration_concept_1.LCEMethodDeclaration(methodName, fqn, (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.PARAMETERS_PROP, method_declaration_concept_1.LCEParameterDeclaration.conceptId, childConcepts), methodType.returnType, methodType.typeParameters, "decorators" in node ? (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts) : [], visibility, methodType.async, code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), "override" in node ? node.override : undefined, inClass ? node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition : undefined, inClass ? (node.static ? node.static : false) : undefined)); } } else if (node.kind === "constructor") { // constructor methodConcept = (0, concept_1.singleEntryConceptMap)(method_declaration_concept_1.LCEConstructorDeclaration.conceptId, new method_declaration_concept_1.LCEConstructorDeclaration(fqn, (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.PARAMETERS_PROP, method_declaration_concept_1.LCEParameterDeclaration.conceptId, childConcepts), (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.PARAMETERS_PROP, method_declaration_concept_1.LCEParameterPropertyDeclaration.conceptId, childConcepts), code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node))); } else if (node.kind === "get") { // getter methodConcept = (0, concept_1.singleEntryConceptMap)(accessor_declaration_concept_1.LCEAccessorProperty.conceptId, new accessor_declaration_concept_1.LCEAccessorProperty(fqn, methodName, new accessor_declaration_concept_1.LCEGetterDeclaration(methodType.returnType, "decorators" in node ? (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts) : [], visibility, code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), "override" in node ? node.override : undefined, inClass ? node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition : undefined, inClass ? (node.static ? node.static : false) : undefined))); } else { // setter methodConcept = (0, concept_1.singleEntryConceptMap)(accessor_declaration_concept_1.LCEAccessorProperty.conceptId, new accessor_declaration_concept_1.LCEAccessorProperty(fqn, methodName, undefined, new accessor_declaration_concept_1.LCESetterDeclaration((0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.PARAMETERS_PROP, method_declaration_concept_1.LCEParameterDeclaration.conceptId, childConcepts), "decorators" in node ? (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.MethodTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts) : [], visibility, code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), "override" in node ? node.override : undefined, inClass ? node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition : undefined, inClass ? (node.static ? node.static : false) : undefined))); } return (0, concept_1.mergeConceptMaps)(methodConcept, dependency_resolution_processor_1.DependencyResolutionProcessor.getRegisteredDependencies(localContexts)); } } return new Map(); } } exports.MethodProcessor = MethodProcessor; /** * Extracts concepts for method parameters of globally declared (abstract) classes and interfaces */ class MethodParameterProcessor extends processor_1.Processor { executionCondition = new execution_condition_1.ExecutionCondition([utils_1.AST_NODE_TYPES.Identifier, utils_1.AST_NODE_TYPES.TSParameterProperty], // TODO: add other parameter patterns ({ localContexts }) => { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_PARAMETERS); const parentPropName = (0, processor_utils_1.getParentPropName)(localContexts); return !!flagContext && flagContext[1] == flagContext[0] && (parentPropName === method_traverser_1.MethodTraverser.PARAMETERS_PROP || parentPropName === function_traverser_1.FunctionTraverser.PARAMETERS_PROP); }); postChildrenProcessing({ node, localContexts, globalContext }, childConcepts) { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_PARAMETERS); const methodTypeContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.METHOD_TYPE); // only process parameters, if the required function type could be determined for the method that set the processing flag if (methodTypeContext && methodTypeContext[1] === flagContext[1]) { const methodType = methodTypeContext[0]; const paramIndex = (0, processor_utils_1.getParentPropIndex)(localContexts); if (paramIndex !== undefined && methodType.parameters[paramIndex]) { const methodTypeParam = methodType.parameters[paramIndex]; if (node.type === utils_1.AST_NODE_TYPES.Identifier) { const scopeFqn = dependency_resolution_processor_1.DependencyResolutionProcessor.constructScopeFQN(localContexts); dependency_resolution_processor_1.DependencyResolutionProcessor.registerDeclaration(localContexts, methodTypeParam.name, new context_1.FQN(scopeFqn.globalFqn + "." + methodTypeParam.name, scopeFqn.localFqn + "." + methodTypeParam.name)); return (0, concept_1.singleEntryConceptMap)(method_declaration_concept_1.LCEParameterDeclaration.conceptId, new method_declaration_concept_1.LCEParameterDeclaration(methodTypeParam.index, methodTypeParam.name, methodTypeParam.type, "optional" in node && !!node.optional, (0, processor_utils_1.getAndDeleteChildConcepts)(expression_traverser_1.IdentifierTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts), code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node))); } else if (node.type === utils_1.AST_NODE_TYPES.TSParameterProperty) { const scopeFqn = dependency_resolution_processor_1.DependencyResolutionProcessor.constructScopeFQN(localContexts, true); const paramPropFQN = new context_1.FQN(scopeFqn.globalFqn.replace(".constructor", "") + "." + methodTypeParam.name, scopeFqn.localFqn.replace(".constructor", "") + "." + methodTypeParam.name); const paramPropConcept = (0, concept_1.singleEntryConceptMap)(method_declaration_concept_1.LCEParameterPropertyDeclaration.conceptId, new method_declaration_concept_1.LCEParameterPropertyDeclaration(methodTypeParam.index, methodTypeParam.name, paramPropFQN, "optional" in node.parameter && node.parameter.optional, methodTypeParam.type, (0, processor_utils_1.getChildConcepts)(method_traverser_1.ParameterPropertyTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts), node.accessibility ?? "public", node.readonly, code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), node.override ?? false)); const paramConcept = (0, concept_1.singleEntryConceptMap)(method_declaration_concept_1.LCEParameterDeclaration.conceptId, new method_declaration_concept_1.LCEParameterDeclaration(methodTypeParam.index, methodTypeParam.name, methodTypeParam.type, "optional" in node.parameter && node.parameter.optional, (0, processor_utils_1.getAndDeleteChildConcepts)(method_traverser_1.ParameterPropertyTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts), code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node))); dependency_resolution_processor_1.DependencyResolutionProcessor.registerDeclaration(localContexts, methodTypeParam.name, paramPropFQN, true); return (0, concept_1.mergeConceptMaps)(paramConcept, paramPropConcept); } } } return new Map(); } } exports.MethodParameterProcessor = MethodParameterProcessor; class PropertyProcessor extends processor_1.Processor { executionCondition = new execution_condition_1.ExecutionCondition([utils_1.AST_NODE_TYPES.PropertyDefinition, utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition, utils_1.AST_NODE_TYPES.TSPropertySignature], ({ localContexts }) => { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_CLASS_LIKE_MEMBERS); return !!flagContext && flagContext[1] == flagContext[0]; // e.g. Property(0) <- ClassBody(1) <- ClassDeclaration(2) (same for interface) }); preChildrenProcessing({ node, localContexts }) { if ((node.type === utils_1.AST_NODE_TYPES.PropertyDefinition || node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition || node.type === utils_1.AST_NODE_TYPES.TSPropertySignature) && !node.computed) { const [propertyName] = processMemberName(node.key); dependency_resolution_processor_1.DependencyResolutionProcessor.addScopeContext(localContexts, context_1.FQN.id(propertyName)); dependency_resolution_processor_1.DependencyResolutionProcessor.createDependencyIndex(localContexts); } } postChildrenProcessing({ node, localContexts, globalContext, ...unusedProcessingContext }, childConcepts) { if ((node.type === utils_1.AST_NODE_TYPES.PropertyDefinition || node.type === utils_1.AST_NODE_TYPES.TSPropertySignature || node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition) && !node.computed) { const [propertyName, jsPrivate] = processMemberName(node.key); const fqn = dependency_resolution_processor_1.DependencyResolutionProcessor.constructScopeFQN(localContexts); dependency_resolution_processor_1.DependencyResolutionProcessor.registerDeclaration(localContexts, propertyName, fqn, true); const parentDeclNode = determineParentDeclarationNode(node, localContexts); const inClass = parentDeclNode.type === utils_1.AST_NODE_TYPES.ClassDeclaration || parentDeclNode.type === utils_1.AST_NODE_TYPES.ClassExpression; // indexed access type check to prevent infinite recursion on native type parsing const isIndexedAccessType = (0, processor_utils_1.getAndDeleteChildConcepts)(property_traverser_1.PropertyTraverser.TYPE_ANNOTATION_PROP, type_annotation_concept_1.LCEIndexAccessTypeAnnotation.conceptId, childConcepts).length > 0; const type = isIndexedAccessType ? type_concept_1.LCETypeNotIdentified.INDEXED_ACCESS_TYPE : (0, type_utils_1.parseClassPropertyType)({ globalContext, localContexts, node, ...unusedProcessingContext }, node.key); return (0, concept_1.mergeConceptMaps)((0, concept_1.singleEntryConceptMap)(property_declaration_concept_1.LCEPropertyDeclaration.conceptId, new property_declaration_concept_1.LCEPropertyDeclaration(propertyName, fqn, node.optional, type, "decorators" in node ? (0, processor_utils_1.getAndDeleteChildConcepts)(property_traverser_1.PropertyTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts) : [], jsPrivate ? "js_private" : (node.accessibility ?? "public"), node.readonly, code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), "override" in node ? node.override : undefined, inClass ? node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition : undefined, inClass ? (node.static ? node.static : false) : undefined)), dependency_resolution_processor_1.DependencyResolutionProcessor.getRegisteredDependencies(localContexts)); } return new Map(); } } exports.PropertyProcessor = PropertyProcessor; class AutoAccessorDeclarationProcessor extends processor_1.Processor { executionCondition = new execution_condition_1.ExecutionCondition([utils_1.AST_NODE_TYPES.AccessorProperty, utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty], ({ localContexts }) => { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_CLASS_LIKE_MEMBERS); return !!flagContext && flagContext[1] == flagContext[0]; // e.g. AccessorProperty(0) <- ClassBody(1) <- ClassDeclaration(2) (same for interface) }); preChildrenProcessing({ node, localContexts }) { if ((node.type === utils_1.AST_NODE_TYPES.AccessorProperty || node.type === utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty) && !node.computed) { const [accessorPropertyName] = processMemberName(node.key); dependency_resolution_processor_1.DependencyResolutionProcessor.addScopeContext(localContexts, context_1.FQN.id(accessorPropertyName)); dependency_resolution_processor_1.DependencyResolutionProcessor.createDependencyIndex(localContexts); } } postChildrenProcessing({ node, localContexts, globalContext, ...unusedProcessingContext }, childConcepts) { if ((node.type === utils_1.AST_NODE_TYPES.AccessorProperty || node.type === utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty) && !node.computed) { const [accessorPropertyName, jsPrivate] = processMemberName(node.key); const fqn = dependency_resolution_processor_1.DependencyResolutionProcessor.constructScopeFQN(localContexts); dependency_resolution_processor_1.DependencyResolutionProcessor.registerDeclaration(localContexts, accessorPropertyName, fqn, true); return (0, concept_1.mergeConceptMaps)((0, concept_1.singleEntryConceptMap)(accessor_declaration_concept_1.LCEAccessorProperty.conceptId, new accessor_declaration_concept_1.LCEAccessorProperty(fqn, accessorPropertyName, undefined, undefined, new accessor_declaration_concept_1.LCEAutoAccessorDeclaration((0, type_utils_1.parseClassPropertyType)({ globalContext, localContexts, node, ...unusedProcessingContext }, node.key), "decorators" in node ? (0, processor_utils_1.getAndDeleteChildConcepts)(property_traverser_1.PropertyTraverser.DECORATORS_PROP, decorator_concept_1.LCEDecorator.conceptId, childConcepts) : [], jsPrivate ? "js_private" : (node.accessibility ?? "public"), code_coordinate_utils_1.CodeCoordinateUtils.getCodeCoordinates(globalContext, node), node.override, node.type === utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty, node.static ? node.static : false))), dependency_resolution_processor_1.DependencyResolutionProcessor.getRegisteredDependencies(localContexts)); } return new Map(); } } exports.AutoAccessorDeclarationProcessor = AutoAccessorDeclarationProcessor; /** * Returns the field or method name for a given non-computed class element. * Also returns if the element was declared private by using the # prefix */ function processMemberName(name) { let result; let jsPrivate = false; if (name.type === utils_1.AST_NODE_TYPES.Identifier) { result = name.name; } else if (name.type === utils_1.AST_NODE_TYPES.Literal) { result = name.value + ""; } else { result = name.name; jsPrivate = true; } return [result, jsPrivate]; } /** * Determine node of ancestor that triggered the processing of members */ function determineParentDeclarationNode(currentNode, localContexts) { const flagContext = localContexts.getNextContext(context_keys_1.CoreContextKeys.PROCESS_CLASS_LIKE_MEMBERS); let parentDeclNode = currentNode; for (let i = flagContext[1]; i > 0; --i) { if (parentDeclNode.parent) { parentDeclNode = parentDeclNode.parent; } } return parentDeclNode; }