UNPKG

eslint-plugin-mocha

Version:

Eslint rules for mocha.

136 lines 6.21 kB
import { asRuleNode } from "./ast/rule-node.js"; import { applyBindingSourceValue, applyContainerPropertyAssignment as applyTrackedContainerPropertyAssignment, clonePendingPathState, createHandledPendingPathState, createInitialPendingPathState, getTrackedContainerPropertiesFromExpression, haveSamePendingPathStates, isTrackedCallbackExpression, mergeIncomingPendingPathStates } from "./tracked-callback-reference-state.js"; function isHandledHandoffExpression(sourceCode, node, state) { if (isTrackedCallbackExpression(sourceCode, node, state)) { return true; } return (getTrackedContainerPropertiesFromExpression(sourceCode, node, state)?.size ?? 0) > 0; } function callFinishesPendingPaths(sourceCode, node, state) { if (isTrackedCallbackExpression(sourceCode, asRuleNode(node.callee), state)) { return true; } return node.arguments.some(function (argument) { const candidate = argument.type === 'SpreadElement' ? argument.argument : argument; return isHandledHandoffExpression(sourceCode, asRuleNode(candidate), state); }); } function applyBindingAssignment(sourceCode, state, operation) { const nextState = { ...state, aliasBindings: new Set(Array.from(state.aliasBindings).filter(function (binding) { return binding !== operation.target; })), containerPropertiesByBinding: new Map(Array.from(state.containerPropertiesByBinding).filter(function ([binding]) { return binding !== operation.target; })) }; return applyBindingSourceValue(sourceCode, nextState, operation.source, operation.target); } function applyOperation(sourceCode, state, operation) { if (operation.type === 'callbackHandoff') { return createHandledPendingPathState(); } if (operation.type === 'bindingAssignment') { return applyBindingAssignment(sourceCode, state, operation); } if (operation.type === 'call') { return callFinishesPendingPaths(sourceCode, operation.node, state) ? createHandledPendingPathState() : clonePendingPathState(state); } return applyTrackedContainerPropertyAssignment(sourceCode, state, operation); } function runOperations(sourceCode, entryState, operations) { let nextState = clonePendingPathState(entryState); for (const operation of operations) { nextState = applyOperation(sourceCode, nextState, operation); } return nextState; } function computeEntryState(callbackBinding, initialSegmentId, segment, exitStatesBySegmentId) { if (segment.id === initialSegmentId) { return createInitialPendingPathState(callbackBinding); } return mergeIncomingPendingPathStates(segment.prevSegments.map(function (previousSegment) { return exitStatesBySegmentId.get(previousSegment.id) ?? createHandledPendingPathState(); })); } export function enqueueNextSegments(nextSegments, pendingSegments, queuedSegmentIds) { let nextPendingSegments = pendingSegments; let nextQueuedSegmentIds = queuedSegmentIds; nextSegments.forEach(function enqueueNextSegment(nextSegment) { if (!nextQueuedSegmentIds.has(nextSegment.id)) { nextPendingSegments = [...nextPendingSegments, nextSegment]; nextQueuedSegmentIds = new Set([...nextQueuedSegmentIds, nextSegment.id]); } }); return [nextPendingSegments, nextQueuedSegmentIds]; } function segmentHasUnhandledReturnPath(exitStatesBySegmentId, segment) { return (exitStatesBySegmentId.get(segment.id) ?? createHandledPendingPathState()).hasUnhandledPath; } function processPendingSegment(context, segment, exitStatesBySegmentId) { const entryState = computeEntryState(context.callbackBinding, context.codePath.initialSegment.id, segment, exitStatesBySegmentId); return runOperations(context.sourceCode, entryState, context.operationsBySegmentId.get(segment.id) ?? []); } function hasUnprocessedSegments(pendingSegments) { return pendingSegments.length > 0; } function createPendingSegmentQueue(context) { return { exitStatesBySegmentId: new Map(), pendingSegments: [context.codePath.initialSegment], queuedSegmentIds: new Set([context.codePath.initialSegment.id]) }; } function removeQueuedSegmentId(queuedSegmentIds, segmentId) { return new Set(Array.from(queuedSegmentIds).filter(function (queuedSegmentId) { return queuedSegmentId !== segmentId; })); } function dropPendingSegment(queue) { const [segment, ...pendingSegments] = queue.pendingSegments; return [ segment, { ...queue, pendingSegments, queuedSegmentIds: removeQueuedSegmentId(queue.queuedSegmentIds, segment?.id) } ]; } function processKnownPendingSegment(context, queue, segment) { const nextState = processPendingSegment(context, segment, queue.exitStatesBySegmentId); const previousState = queue.exitStatesBySegmentId.get(segment.id); if (previousState !== undefined && haveSamePendingPathStates(previousState, nextState)) { return queue; } const [pendingSegments, queuedSegmentIds] = enqueueNextSegments(segment.nextSegments, queue.pendingSegments, queue.queuedSegmentIds); return { exitStatesBySegmentId: new Map([ ...queue.exitStatesBySegmentId, [segment.id, nextState] ]), pendingSegments, queuedSegmentIds }; } function processAllPendingSegments(context, initialQueue) { let queue = initialQueue; while (hasUnprocessedSegments(queue.pendingSegments)) { const [segment, queueWithoutSegment] = dropPendingSegment(queue); queue = queueWithoutSegment; if (segment !== undefined) { queue = processKnownPendingSegment(context, queue, segment); } } return queue; } export function hasUnhandledReturnPath(context) { const queue = processAllPendingSegments(context, createPendingSegmentQueue(context)); return context.codePath.returnedSegments.some(function (segment) { return segmentHasUnhandledReturnPath(queue.exitStatesBySegmentId, segment); }); } //# sourceMappingURL=done-callback-paths.js.map