@mondaydotcomorg/atp-compiler
Version:
Production-ready compiler for transforming async iteration patterns into resumable operations with checkpoint-based state management
162 lines (132 loc) • 3.73 kB
text/typescript
import * as t from '@babel/types';
import { PAUSABLE_CALL_PATTERNS } from '../types.js';
export function isPausableCall(node: t.Node): boolean {
if (!t.isAwaitExpression(node)) {
return false;
}
const argument = node.argument;
if (!t.isCallExpression(argument)) {
return false;
}
return isPausableCallExpression(argument);
}
export function isPausableCallExpression(node: t.CallExpression): boolean {
const callee = node.callee;
if (!t.isMemberExpression(callee)) {
return false;
}
const fullPath = getMemberExpressionPath(callee);
return PAUSABLE_CALL_PATTERNS.some(
(pattern) => fullPath === `${pattern.namespace}.${pattern.method}`
);
}
export function getMemberExpressionPath(node: t.MemberExpression): string {
const parts: string[] = [];
let current: t.Node = node;
while (t.isMemberExpression(current)) {
if (t.isIdentifier(current.property)) {
parts.unshift(current.property.name);
}
current = current.object;
}
if (t.isIdentifier(current)) {
parts.unshift(current.name);
}
return parts.join('.');
}
export function containsAwait(node: t.Node): boolean {
let hasAwait = false;
const checkNode = (n: t.Node): void => {
if (t.isAwaitExpression(n)) {
hasAwait = true;
return;
}
if (hasAwait) return;
Object.keys(n).forEach((key) => {
const value = (n as any)[key];
if (Array.isArray(value)) {
value.forEach((item) => {
if (item && typeof item === 'object' && item.type) {
checkNode(item);
}
});
} else if (value && typeof value === 'object' && value.type) {
checkNode(value);
}
});
};
checkNode(node);
return hasAwait;
}
export function containsPausableCall(node: t.Node): boolean {
let hasPausable = false;
const checkNode = (n: t.Node): void => {
if (t.isAwaitExpression(n) && isPausableCall(n)) {
hasPausable = true;
return;
}
if (hasPausable) return;
Object.keys(n).forEach((key) => {
const value = (n as any)[key];
if (Array.isArray(value)) {
value.forEach((item) => {
if (item && typeof item === 'object' && item.type) {
checkNode(item);
}
});
} else if (value && typeof value === 'object' && value.type) {
checkNode(value);
}
});
};
checkNode(node);
return hasPausable;
}
export function isAsyncFunction(node: t.Node): boolean {
return (
(t.isFunctionDeclaration(node) ||
t.isFunctionExpression(node) ||
t.isArrowFunctionExpression(node)) &&
node.async === true
);
}
export function getNodeLocation(node: t.Node): { line: number; column: number } | undefined {
if (node.loc) {
return {
line: node.loc.start.line,
column: node.loc.start.column,
};
}
return undefined;
}
export function createRuntimeCall(fnName: string, args: t.Expression[]): t.AwaitExpression {
return t.awaitExpression(
t.callExpression(t.memberExpression(t.identifier('__runtime'), t.identifier(fnName)), args)
);
}
export function wrapInAsyncFunction(body: t.Statement[]): t.FunctionExpression {
return t.functionExpression(null, [], t.blockStatement(body), false, true);
}
export function isArrayMethod(node: t.Node, methodName: string): boolean {
if (!t.isCallExpression(node)) {
return false;
}
const callee = node.callee;
if (!t.isMemberExpression(callee)) {
return false;
}
return t.isIdentifier(callee.property) && callee.property.name === methodName;
}
/**
* Extract parameter name from ForOfStatement left side
*/
export function extractForOfParamName(left: t.VariableDeclaration | t.LVal): string {
if (t.isVariableDeclaration(left)) {
const id = left.declarations[0]?.id;
return t.isIdentifier(id) ? id.name : 'item';
} else if (t.isIdentifier(left)) {
return left.name;
} else {
return 'item';
}
}