ngx-unused
Version:
Detect unused classes (components, pipes, services and directives) defined in Angular workspace.
66 lines (65 loc) • 2.97 kB
JavaScript
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);
}