UNPKG

ngx-unused

Version:

Detect unused classes (components, pipes, services and directives) defined in Angular workspace.

66 lines (65 loc) 2.97 kB
import { SyntaxKind } from 'ts-morph'; export function hasUsagesInTs(declaration) { const referencingNodes = declaration.findReferencesAsNodes().filter(node => { const sourceFile = node.getSourceFile(); return !isFileIrrelevant(sourceFile); }); const isDynamicallyImported = isClassReferencedInDynamicImportCallback(declaration); return (referencingNodes.some(node => isReferecingNodeRelevant(node)) || isDynamicallyImported); } function isFileIrrelevant(sourceFile) { return (sourceFile.isDeclarationFile() || sourceFile.getBaseName().includes('.spec.ts')); } function isDynamicImport(node) { return (node.isKind(SyntaxKind.CallExpression) && !!node.getFirstChildIfKind(SyntaxKind.ImportKeyword)); } function isDynamicImportReferencingClass(node, declaration) { var _a, _b; const importCallback = (_b = (_a = node .getFirstAncestorByKind(SyntaxKind.PropertyAccessExpression)) === null || _a === void 0 ? void 0 : _a.getFirstAncestorByKind(SyntaxKind.ArrowFunction)) === null || _b === void 0 ? void 0 : _b.getFirstDescendantByKind(SyntaxKind.ArrowFunction); return !!(importCallback === null || importCallback === void 0 ? void 0 : importCallback.getDescendants().some(descendant => descendant.getFullText() === declaration.getName())); } function isClassReferencedInDynamicImportCallback(declaration) { return declaration .getSourceFile() .getReferencingNodesInOtherSourceFiles() .filter(reference => !isFileIrrelevant(reference.getSourceFile())) .filter(isDynamicImport) .some(node => isDynamicImportReferencingClass(node, declaration)); } function isReferecingNodeRelevant(node) { const irrelevantNodeKinds = [ SyntaxKind.ImportSpecifier, SyntaxKind.ExportSpecifier, ]; const irrelevantParentNodeKinds = [ SyntaxKind.ExportAssignment, SyntaxKind.ClassDeclaration, SyntaxKind.ImportSpecifier, SyntaxKind.ExportSpecifier, ]; const isOfIrrelevantKind = irrelevantNodeKinds.includes(node.getKind()); const hasParentOfIrrelevantKind = node.getParent() !== undefined && irrelevantParentNodeKinds.includes(node.getParent().getKind()); return ((!isOfIrrelevantKind && !hasParentOfIrrelevantKind && !isInNgModuleDecoratorCall(node)) || node.getParent().isKind(SyntaxKind.PropertyAssignment)); } function isInNgModuleDecoratorCall(node) { if (node.getParent() === undefined) { return false; } if (node.getParent().isKind(SyntaxKind.PropertyAssignment)) { return false; } if (node.getFirstAncestor(ancestor => ancestor.isKind(SyntaxKind.PropertyAssignment) && ancestor.getName() === 'bootstrap') !== undefined) { return false; } return (node.getFirstAncestor(ancestor => ancestor.isKind(SyntaxKind.Decorator) && ancestor.getFullName() === 'NgModule') !== undefined); }