function getSubscribeNodeFrom(observableNode) { return observableNode.callee; } function isSubscribedObservable(observableNode) { try { return observableNode.callee.property.name === 'subscribe'; } catch { return false; } } function getPipeNodeOf(subscribeNode) { try { var pipeNodeRef = subscribeNode.object.callee; if (pipeNodeRef.property.name !== 'pipe') throw new Error(); return pipeNodeRef; } catch { return undefined; } } function getOperatorsFor(pipeNode) { return pipeNode.parent.arguments; } function isReleaseOperator(operatorNode) { return operatorNode.callee.name === 'takeUntil' || operatorNode.callee.name === 'untilDestroyed'; } module.exports = { meta: { type: "problem", docs: { description: "Check every rx.js subscription handling with release operator.", }, fixable: "code", schema: [] }, create: function (context) { return { CallExpression(node) { if (isSubscribedObservable(node)) { const subscribeNode = getSubscribeNodeFrom(node); const pipeNode = getPipeNodeOf(subscribeNode); if (pipeNode) { const operators = getOperatorsFor(pipeNode); if (operators.length) { if (!isReleaseOperator(operators[operators.length - 1])) { context.report({ node, message: 'Last operator in pipe should release subscription. Example: `subj$.pipe(operator1(), operator2(), takeUntil(destroy$))`' }); } } else { context.report({ node, message: 'Pipe associated with subscribed observable should have release operator such `takeUntil(destroy$)`' }); } } else { context.report({ node, message: 'Subscriptions Should be piped with release operator' }); } } } }; } };