UNPKG

jest-preset-angular

Version:

Jest preset configuration for Angular projects

510 lines (509 loc) 20.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.reflectObjectLiteral = exports.findMember = exports.filterToMembersWithDecorator = exports.reflectTypeEntityToDeclaration = exports.reflectIdentifierOfDeclaration = exports.reflectNameOfDeclaration = exports.TypeScriptReflectionHost = void 0; const tslib_1 = require("tslib"); const typescript_1 = (0, tslib_1.__importDefault)(require("typescript")); const host_1 = require("./host"); const type_to_value_1 = require("./type_to_value"); const util_1 = require("./util"); class TypeScriptReflectionHost { constructor(checker) { this.checker = checker; } getDecoratorsOfDeclaration(declaration) { if (declaration.decorators === undefined || declaration.decorators.length === 0) { return null; } return declaration.decorators.map(decorator => this._reflectDecorator(decorator)) .filter((dec) => dec !== null); } getMembersOfClass(clazz) { const tsClazz = castDeclarationToClassOrDie(clazz); return tsClazz.members.map(member => this._reflectMember(member)) .filter((member) => member !== null); } getConstructorParameters(clazz) { const tsClazz = castDeclarationToClassOrDie(clazz); const isDeclaration = tsClazz.getSourceFile().isDeclarationFile; const ctor = tsClazz.members.find((member) => typescript_1.default.isConstructorDeclaration(member) && (isDeclaration || member.body !== undefined)); if (ctor === undefined) { return null; } return ctor.parameters.map(node => { const name = parameterName(node.name); const decorators = this.getDecoratorsOfDeclaration(node); let originalTypeNode = node.type || null; let typeNode = originalTypeNode; if (typeNode && typescript_1.default.isUnionTypeNode(typeNode)) { let childTypeNodes = typeNode.types.filter(childTypeNode => !(typescript_1.default.isLiteralTypeNode(childTypeNode) && childTypeNode.literal.kind === typescript_1.default.SyntaxKind.NullKeyword)); if (childTypeNodes.length === 1) { typeNode = childTypeNodes[0]; } } const typeValueReference = (0, type_to_value_1.typeToValue)(typeNode, this.checker); return { name, nameNode: node.name, typeValueReference, typeNode: originalTypeNode, decorators, }; }); } getImportOfIdentifier(id) { const directImport = this.getDirectImportOfIdentifier(id); if (directImport !== null) { return directImport; } else if (typescript_1.default.isQualifiedName(id.parent) && id.parent.right === id) { return this.getImportOfNamespacedIdentifier(id, getQualifiedNameRoot(id.parent)); } else if (typescript_1.default.isPropertyAccessExpression(id.parent) && id.parent.name === id) { return this.getImportOfNamespacedIdentifier(id, getFarLeftIdentifier(id.parent)); } else { return null; } } getExportsOfModule(node) { if (!typescript_1.default.isSourceFile(node)) { throw new Error(`getExportsOfModule() called on non-SourceFile in TS code`); } const symbol = this.checker.getSymbolAtLocation(node); if (symbol === undefined) { return null; } const map = new Map(); this.checker.getExportsOfModule(symbol).forEach(exportSymbol => { const decl = this.getDeclarationOfSymbol(exportSymbol, null); if (decl !== null) { map.set(exportSymbol.name, decl); } }); return map; } isClass(node) { return (0, util_1.isNamedClassDeclaration)(node); } hasBaseClass(clazz) { return this.getBaseClassExpression(clazz) !== null; } getBaseClassExpression(clazz) { if (!(typescript_1.default.isClassDeclaration(clazz) || typescript_1.default.isClassExpression(clazz)) || clazz.heritageClauses === undefined) { return null; } const extendsClause = clazz.heritageClauses.find(clause => clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword); if (extendsClause === undefined) { return null; } const extendsType = extendsClause.types[0]; if (extendsType === undefined) { return null; } return extendsType.expression; } getDeclarationOfIdentifier(id) { let symbol = this.checker.getSymbolAtLocation(id); if (symbol === undefined) { return null; } return this.getDeclarationOfSymbol(symbol, id); } getDefinitionOfFunction(node) { if (!typescript_1.default.isFunctionDeclaration(node) && !typescript_1.default.isMethodDeclaration(node) && !typescript_1.default.isFunctionExpression(node)) { return null; } return { node, body: node.body !== undefined ? Array.from(node.body.statements) : null, parameters: node.parameters.map(param => { const name = parameterName(param.name); const initializer = param.initializer || null; return { name, node: param, initializer }; }), }; } getGenericArityOfClass(clazz) { if (!typescript_1.default.isClassDeclaration(clazz)) { return null; } return clazz.typeParameters !== undefined ? clazz.typeParameters.length : 0; } getVariableValue(declaration) { return declaration.initializer || null; } getDtsDeclaration(_) { return null; } getInternalNameOfClass(clazz) { return clazz.name; } getAdjacentNameOfClass(clazz) { return clazz.name; } isStaticallyExported(decl) { let topLevel = decl; if (typescript_1.default.isVariableDeclaration(decl) && typescript_1.default.isVariableDeclarationList(decl.parent)) { topLevel = decl.parent.parent; } if (topLevel.modifiers !== undefined && topLevel.modifiers.some(modifier => modifier.kind === typescript_1.default.SyntaxKind.ExportKeyword)) { return true; } if (topLevel.parent === undefined || !typescript_1.default.isSourceFile(topLevel.parent)) { return false; } const localExports = this.getLocalExportedDeclarationsOfSourceFile(decl.getSourceFile()); return localExports.has(decl); } getDirectImportOfIdentifier(id) { const symbol = this.checker.getSymbolAtLocation(id); if (symbol === undefined || symbol.declarations === undefined || symbol.declarations.length !== 1) { return null; } const decl = symbol.declarations[0]; const importDecl = getContainingImportDeclaration(decl); if (importDecl === null) { return null; } if (!typescript_1.default.isStringLiteral(importDecl.moduleSpecifier)) { return null; } return { from: importDecl.moduleSpecifier.text, name: getExportedName(decl, id) }; } getImportOfNamespacedIdentifier(id, namespaceIdentifier) { if (namespaceIdentifier === null) { return null; } const namespaceSymbol = this.checker.getSymbolAtLocation(namespaceIdentifier); if (!namespaceSymbol || namespaceSymbol.declarations === undefined) { return null; } const declaration = namespaceSymbol.declarations.length === 1 ? namespaceSymbol.declarations[0] : null; if (!declaration) { return null; } const namespaceDeclaration = typescript_1.default.isNamespaceImport(declaration) ? declaration : null; if (!namespaceDeclaration) { return null; } const importDeclaration = namespaceDeclaration.parent.parent; if (!typescript_1.default.isStringLiteral(importDeclaration.moduleSpecifier)) { return null; } return { from: importDeclaration.moduleSpecifier.text, name: id.text, }; } getDeclarationOfSymbol(symbol, originalId) { let valueDeclaration = undefined; if (symbol.valueDeclaration !== undefined) { valueDeclaration = symbol.valueDeclaration; } else if (symbol.declarations !== undefined && symbol.declarations.length > 0) { valueDeclaration = symbol.declarations[0]; } if (valueDeclaration !== undefined && typescript_1.default.isShorthandPropertyAssignment(valueDeclaration)) { const shorthandSymbol = this.checker.getShorthandAssignmentValueSymbol(valueDeclaration); if (shorthandSymbol === undefined) { return null; } return this.getDeclarationOfSymbol(shorthandSymbol, originalId); } else if (valueDeclaration !== undefined && typescript_1.default.isExportSpecifier(valueDeclaration)) { const targetSymbol = this.checker.getExportSpecifierLocalTargetSymbol(valueDeclaration); if (targetSymbol === undefined) { return null; } return this.getDeclarationOfSymbol(targetSymbol, originalId); } const importInfo = originalId && this.getImportOfIdentifier(originalId); const viaModule = importInfo !== null && importInfo.from !== null && !importInfo.from.startsWith('.') ? importInfo.from : null; while (symbol.flags & typescript_1.default.SymbolFlags.Alias) { symbol = this.checker.getAliasedSymbol(symbol); } if (symbol.valueDeclaration !== undefined) { return { node: symbol.valueDeclaration, known: null, viaModule, identity: null, kind: 0, }; } else if (symbol.declarations !== undefined && symbol.declarations.length > 0) { return { node: symbol.declarations[0], known: null, viaModule, identity: null, kind: 0, }; } else { return null; } } _reflectDecorator(node) { let decoratorExpr = node.expression; let args = null; if (typescript_1.default.isCallExpression(decoratorExpr)) { args = Array.from(decoratorExpr.arguments); decoratorExpr = decoratorExpr.expression; } if (!(0, host_1.isDecoratorIdentifier)(decoratorExpr)) { return null; } const decoratorIdentifier = typescript_1.default.isIdentifier(decoratorExpr) ? decoratorExpr : decoratorExpr.name; const importDecl = this.getImportOfIdentifier(decoratorIdentifier); return { name: decoratorIdentifier.text, identifier: decoratorExpr, import: importDecl, node, args, }; } _reflectMember(node) { let kind = null; let value = null; let name = null; let nameNode = null; if (typescript_1.default.isPropertyDeclaration(node)) { kind = host_1.ClassMemberKind.Property; value = node.initializer || null; } else if (typescript_1.default.isGetAccessorDeclaration(node)) { kind = host_1.ClassMemberKind.Getter; } else if (typescript_1.default.isSetAccessorDeclaration(node)) { kind = host_1.ClassMemberKind.Setter; } else if (typescript_1.default.isMethodDeclaration(node)) { kind = host_1.ClassMemberKind.Method; } else if (typescript_1.default.isConstructorDeclaration(node)) { kind = host_1.ClassMemberKind.Constructor; } else { return null; } if (typescript_1.default.isConstructorDeclaration(node)) { name = 'constructor'; } else if (typescript_1.default.isIdentifier(node.name)) { name = node.name.text; nameNode = node.name; } else if (typescript_1.default.isStringLiteral(node.name)) { name = node.name.text; nameNode = node.name; } else { return null; } const decorators = this.getDecoratorsOfDeclaration(node); const isStatic = node.modifiers !== undefined && node.modifiers.some(mod => mod.kind === typescript_1.default.SyntaxKind.StaticKeyword); return { node, implementation: node, kind, type: node.type || null, name, nameNode, decorators, value, isStatic, }; } getLocalExportedDeclarationsOfSourceFile(file) { const cacheSf = file; if (cacheSf[LocalExportedDeclarations] !== undefined) { return cacheSf[LocalExportedDeclarations]; } const exportSet = new Set(); cacheSf[LocalExportedDeclarations] = exportSet; const sfSymbol = this.checker.getSymbolAtLocation(cacheSf); if (sfSymbol === undefined || sfSymbol.exports === undefined) { return exportSet; } const iter = sfSymbol.exports.values(); let item = iter.next(); while (item.done !== true) { let exportedSymbol = item.value; if (exportedSymbol.flags & typescript_1.default.SymbolFlags.Alias) { exportedSymbol = this.checker.getAliasedSymbol(exportedSymbol); } if (exportedSymbol.valueDeclaration !== undefined && exportedSymbol.valueDeclaration.getSourceFile() === file) { exportSet.add(exportedSymbol.valueDeclaration); } item = iter.next(); } return exportSet; } } exports.TypeScriptReflectionHost = TypeScriptReflectionHost; function reflectNameOfDeclaration(decl) { const id = reflectIdentifierOfDeclaration(decl); return id && id.text || null; } exports.reflectNameOfDeclaration = reflectNameOfDeclaration; function reflectIdentifierOfDeclaration(decl) { if (typescript_1.default.isClassDeclaration(decl) || typescript_1.default.isFunctionDeclaration(decl)) { return decl.name || null; } else if (typescript_1.default.isVariableDeclaration(decl)) { if (typescript_1.default.isIdentifier(decl.name)) { return decl.name; } } return null; } exports.reflectIdentifierOfDeclaration = reflectIdentifierOfDeclaration; function reflectTypeEntityToDeclaration(type, checker) { let realSymbol = checker.getSymbolAtLocation(type); if (realSymbol === undefined) { throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`); } while (realSymbol.flags & typescript_1.default.SymbolFlags.Alias) { realSymbol = checker.getAliasedSymbol(realSymbol); } let node = null; if (realSymbol.valueDeclaration !== undefined) { node = realSymbol.valueDeclaration; } else if (realSymbol.declarations !== undefined && realSymbol.declarations.length === 1) { node = realSymbol.declarations[0]; } else { throw new Error(`Cannot resolve type entity symbol to declaration`); } if (typescript_1.default.isQualifiedName(type)) { if (!typescript_1.default.isIdentifier(type.left)) { throw new Error(`Cannot handle qualified name with non-identifier lhs`); } const symbol = checker.getSymbolAtLocation(type.left); if (symbol === undefined || symbol.declarations === undefined || symbol.declarations.length !== 1) { throw new Error(`Cannot resolve qualified type entity lhs to symbol`); } const decl = symbol.declarations[0]; if (typescript_1.default.isNamespaceImport(decl)) { const clause = decl.parent; const importDecl = clause.parent; if (!typescript_1.default.isStringLiteral(importDecl.moduleSpecifier)) { throw new Error(`Module specifier is not a string`); } return { node, from: importDecl.moduleSpecifier.text }; } else if (typescript_1.default.isModuleDeclaration(decl)) { return { node, from: null }; } else { throw new Error(`Unknown import type?`); } } else { return { node, from: null }; } } exports.reflectTypeEntityToDeclaration = reflectTypeEntityToDeclaration; function filterToMembersWithDecorator(members, name, module) { return members.filter(member => !member.isStatic) .map(member => { if (member.decorators === null) { return null; } const decorators = member.decorators.filter(dec => { if (dec.import !== null) { return dec.import.name === name && (module === undefined || dec.import.from === module); } else { return dec.name === name && module === undefined; } }); if (decorators.length === 0) { return null; } return { member, decorators }; }) .filter((value) => value !== null); } exports.filterToMembersWithDecorator = filterToMembersWithDecorator; function findMember(members, name, isStatic = false) { return members.find(member => member.isStatic === isStatic && member.name === name) || null; } exports.findMember = findMember; function reflectObjectLiteral(node) { const map = new Map(); node.properties.forEach(prop => { if (typescript_1.default.isPropertyAssignment(prop)) { const name = propertyNameToString(prop.name); if (name === null) { return; } map.set(name, prop.initializer); } else if (typescript_1.default.isShorthandPropertyAssignment(prop)) { map.set(prop.name.text, prop.name); } else { return; } }); return map; } exports.reflectObjectLiteral = reflectObjectLiteral; function castDeclarationToClassOrDie(declaration) { if (!typescript_1.default.isClassDeclaration(declaration)) { throw new Error(`Reflecting on a ${typescript_1.default.SyntaxKind[declaration.kind]} instead of a ClassDeclaration.`); } return declaration; } function parameterName(name) { if (typescript_1.default.isIdentifier(name)) { return name.text; } else { return null; } } function propertyNameToString(node) { if (typescript_1.default.isIdentifier(node) || typescript_1.default.isStringLiteral(node) || typescript_1.default.isNumericLiteral(node)) { return node.text; } else { return null; } } function getQualifiedNameRoot(qualifiedName) { while (typescript_1.default.isQualifiedName(qualifiedName.left)) { qualifiedName = qualifiedName.left; } return typescript_1.default.isIdentifier(qualifiedName.left) ? qualifiedName.left : null; } function getFarLeftIdentifier(propertyAccess) { while (typescript_1.default.isPropertyAccessExpression(propertyAccess.expression)) { propertyAccess = propertyAccess.expression; } return typescript_1.default.isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null; } function getContainingImportDeclaration(node) { return typescript_1.default.isImportSpecifier(node) ? node.parent.parent.parent : typescript_1.default.isNamespaceImport(node) ? node.parent.parent : null; } function getExportedName(decl, originalId) { return typescript_1.default.isImportSpecifier(decl) ? (decl.propertyName !== undefined ? decl.propertyName : decl.name).text : originalId.text; } const LocalExportedDeclarations = Symbol('LocalExportedDeclarations');