@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
JavaScript
"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;
}