UNPKG

@mondaydotcomorg/atp-compiler

Version:

Production-ready compiler for transforming async iteration patterns into resumable operations with checkpoint-based state management

141 lines 5.72 kB
import { parse } from '@babel/parser'; import _traverse from '@babel/traverse'; // @ts-ignore - CommonJS/ESM compatibility const traverse = typeof _traverse.default === 'function' ? _traverse.default : _traverse; import * as t from '@babel/types'; import { containsAwait, isArrayMethod, isPausableCallExpression } from './utils.js'; export class AsyncIterationDetector { detect(code) { const patterns = []; let batchableParallel = false; try { const ast = parse(code, { sourceType: 'module', plugins: ['typescript'], allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true, }); traverse(ast, { ForOfStatement: (path) => { if (containsAwait(path.node.body)) { patterns.push('for-of-await'); } }, WhileStatement: (path) => { if (containsAwait(path.node.body)) { patterns.push('while-await'); } }, CallExpression: (path) => { const node = path.node; if (isArrayMethod(node, 'map')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('map-async'); } } if (isArrayMethod(node, 'forEach')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('forEach-async'); } } if (isArrayMethod(node, 'filter')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('filter-async'); } } if (isArrayMethod(node, 'reduce')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('reduce-async'); } } if (isArrayMethod(node, 'find')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('find-async'); } } if (isArrayMethod(node, 'some')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('some-async'); } } if (isArrayMethod(node, 'every')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('every-async'); } } if (isArrayMethod(node, 'flatMap')) { const callback = node.arguments[0]; if (callback && t.isFunction(callback) && callback.async) { patterns.push('flatMap-async'); } } if (this.isPromiseAll(node)) { patterns.push('promise-all'); if (this.canBatchPromiseAll(node)) { batchableParallel = true; } } if (this.isPromiseAllSettled(node)) { patterns.push('promise-allSettled'); } }, }); return { needsTransform: patterns.length > 0, patterns: [...new Set(patterns)], batchableParallel, }; } catch (error) { return { needsTransform: false, patterns: [], batchableParallel: false, }; } } isPromiseAll(node) { const callee = node.callee; return (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: 'Promise' }) && t.isIdentifier(callee.property, { name: 'all' })); } isPromiseAllSettled(node) { const callee = node.callee; return (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: 'Promise' }) && t.isIdentifier(callee.property, { name: 'allSettled' })); } canBatchPromiseAll(node) { const arrayArg = node.arguments[0]; if (!t.isArrayExpression(arrayArg)) { return false; } if (arrayArg.elements.length === 0) { return false; } return arrayArg.elements.every((el) => { if (!el || t.isSpreadElement(el)) { return false; } return this.isDirectPausableCall(el); }); } isDirectPausableCall(node) { if (t.isAwaitExpression(node)) { node = node.argument; } if (!t.isCallExpression(node)) { return false; } return isPausableCallExpression(node); } } //# sourceMappingURL=detector.js.map