Skip to content

Commit 05bdcb8

Browse files
authored
Merge pull request #79 from code0-tech/feat/#78
Static rendering for if and if-else
2 parents 2ca3a80 + 1929125 commit 05bdcb8

3 files changed

Lines changed: 89 additions & 71 deletions

File tree

src/utils.ts

Lines changed: 25 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import {
1010
} from "@code0-tech/sagittarius-graphql-types";
1111
import ts from "typescript";
1212
import {createSystem, createVirtualTypeScriptEnvironment, VirtualTypeScriptEnvironment} from "@typescript/vfs"
13-
import {DataTypeVariant, getTypeVariant} from "./extraction/getTypeVariant";
14-
import {getTypesFromFunction} from "./extraction/getTypesFromFunction";
1513
import {stringify} from "lossless-json";
1614

1715
/**
@@ -113,59 +111,14 @@ export function generateFlowSourceCode(
113111
const funcMap = new Map(functions?.map(f => [f.identifier, f]));
114112
const visited = new Set<string>();
115113

116-
const generateNodeCall = (nodeId: string, parentNodeId?: string, parentParamIndex?: number): string => {
117-
const node = nodes.find(n => n?.id === nodeId);
118-
if (!node || !node.functionDefinition?.identifier) return "undefined";
119-
120-
const params = (node.parameters?.nodes as NodeParameter[]) || [];
121-
const args = params.map((p, paramIdx) => {
122-
const val = p.value;
123-
if (!val) return isForInference ? `/* @pos ${nodeId} ${paramIdx} */ {}` : `/* @pos ${nodeId} ${paramIdx} */ undefined`;
124-
if (val.__typename === "ReferenceValue") {
125-
const ref = val as ReferenceValue;
126-
let refCode = typeof ref.inputIndex === "number"
127-
? `p_${sanitizeId(ref.nodeFunctionId ?? "undefined")}_${ref.parameterIndex}[${ref.inputIndex}]`
128-
: ref.nodeFunctionId ? `node_${sanitizeId(ref.nodeFunctionId)}` : `flow_${sanitizeId(flow?.id ?? "undefined")}`;
129-
ref.referencePath?.forEach(pathObj => {
130-
refCode += `?.${pathObj.path}`;
131-
});
132-
return `/* @pos ${nodeId} ${paramIdx} */ ${refCode}`;
133-
}
134-
if (val.__typename === "LiteralValue") {
135-
const jsonString = stringify(val?.value)
136-
return `/* @pos ${nodeId} ${paramIdx} */ ${jsonString}`;
137-
}
138-
if (val.__typename === "NodeFunctionIdWrapper") {
139-
const wrapper = val as NodeFunctionIdWrapper;
140-
return generateNodeCall(wrapper.id!, nodeId, paramIdx);
141-
}
142-
return isForInference ? `/* @pos ${nodeId} ${paramIdx} */ ({} as any)` : `/* @pos ${nodeId} ${paramIdx} */ undefined`;
143-
}).join(", ");
144-
145-
const funcName = `/* @pos ${nodeId} null */ fn_${node.functionDefinition.identifier.replace(/::/g, '_')}`;
146-
const call = `${funcName}(${args})`;
147-
// Add position comment only for nested calls (when called from within an argument)
148-
if (parentNodeId !== undefined && parentParamIndex !== undefined) {
149-
return `${call}`;
150-
}
151-
return call;
152-
};
153-
154114
const generateNodeCode = (nodeId: string, indent: string = ""): string => {
155-
if (visited.has(nodeId)) return "";
156115
const node = nodes.find(n => n?.id === nodeId);
157116
if (!node || !node.functionDefinition) return "";
158117
visited.add(nodeId);
159118

160119
const funcDef = funcMap.get(node.functionDefinition.identifier);
161120
if (!funcDef) return `${indent}// Error: Function ${node.functionDefinition.identifier} not found\n`;
162121

163-
// Only use getTypesFromNode if we are NOT already doing inference to avoid infinite recursion
164-
let nodeTypes: any = {parameters: []};
165-
if (!isForInference) {
166-
nodeTypes = getTypesFromFunction(funcDef);
167-
}
168-
169122
const params = (node.parameters?.nodes as NodeParameter[]) || [];
170123
const args = params.map((p, index) => {
171124
const val = p.value;
@@ -186,35 +139,37 @@ export function generateFlowSourceCode(
186139
}
187140
if (val.__typename === "NodeFunctionIdWrapper") {
188141
const wrapper = val as NodeFunctionIdWrapper;
189-
190-
if (!isForInference) {
191-
const expectedType = nodeTypes.parameters[index];
192-
const isFunctionType = expectedType ? getTypeVariant(expectedType, dataTypes)[0].variant === DataTypeVariant.NODE : false;
193-
194-
if (isFunctionType) {
195-
const lambdaArgName = `p_${sanitizeId(nodeId)}_${index}`;
196-
const subTreeCode = generateNodeCode(wrapper.id!, indent + " ");
197-
return `/* @pos ${nodeId} ${index} */ (...${lambdaArgName}) => {\n${subTreeCode}${indent}}`;
198-
} else {
199-
const nestedCall = generateNodeCall(wrapper.id!, nodeId, index);
200-
return `/* @pos ${nodeId} ${index} */ ${nestedCall}`;
201-
}
202-
} else {
203-
// During inference, we just need something valid.
204-
// Defaulting to a lambda is safer for type inference of the parent node's parameters.
205-
const lambdaArgName = `p_${sanitizeId(nodeId)}_${index}`;
206-
const subTreeCode = generateNodeCode(wrapper.id!, indent + " ");
207-
return `/* @pos ${nodeId} ${index} */ (...${lambdaArgName}) => {\n${subTreeCode}${indent}}`;
208-
}
142+
const lambdaArgName = `p_${sanitizeId(nodeId)}_${index}`;
143+
const subTreeCode = generateNodeCode(wrapper.id!, indent + " ");
144+
return `/* @pos ${nodeId} ${index} */ (...${lambdaArgName}) => {\n${subTreeCode}${indent}}`;
209145
}
210146
return isForInference ? `/* @pos ${nodeId} ${index} */ {}` : `/* @pos ${nodeId} ${index} */ undefined`;
211-
}).join(", ");
147+
});
212148

213149
const varName = `node_${sanitizeId(node.id!)}`;
214150
const funcName = `fn_${node?.functionDefinition?.identifier?.replace(/::/g, '_')}`;
215151
const needsAnyCast = args.includes("undefined");
216-
const isReturnNode = node.functionDefinition.identifier === "std::control::return";
217-
let code = `${indent}${isReturnNode ? "return " : `const ${varName} = `}/* @pos ${nodeId} null */ ${funcName}(${args})${needsAnyCast ? "" : ""} ;\n`;
152+
153+
154+
155+
let code = `${indent}`;
156+
157+
if (node.functionDefinition.identifier === "std::control::return") {
158+
code += `return /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
159+
} else if (node.functionDefinition.identifier === "std::control::if") {
160+
code += `/* @pos ${nodeId} null */ if(${args[0]}) {
161+
${generateNodeCode((node.parameters?.nodes?.[1]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
162+
}`
163+
} else if (node.functionDefinition.identifier === "std::control::if_else") {
164+
code += `/* @pos ${nodeId} null */ if(${args[0]}) {
165+
${generateNodeCode((node.parameters?.nodes?.[1]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
166+
} else {
167+
${generateNodeCode((node.parameters?.nodes?.[2]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
168+
}`
169+
} else {
170+
code += `const ${varName} = /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
171+
}
172+
218173
if (node.nextNodeId) code += generateNodeCode(node.nextNodeId, indent);
219174
return code;
220175
};

src/validation/getFlowValidation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const getFlowValidation = (
1111
dataTypes?: DataType[]
1212
): ValidationResult => {
1313

14-
1514
if (!flow?.startingNodeId) {
1615
return {
1716
isValid: false,

test/flowValidation.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,4 +1594,68 @@ describe('getFlowValidation - Integrationstest', () => {
15941594
expect(result.diagnostics[0].parameterIndex).toBe(null)
15951595
});
15961596

1597+
it('13', () => {
1598+
1599+
const flow: Flow = {
1600+
startingNodeId: "gid://sagittarius/NodeFunction/1",
1601+
nodes: {
1602+
nodes: [
1603+
{
1604+
id: "gid://sagittarius/NodeFunction/1",
1605+
functionDefinition: {identifier: "std::control::if"},
1606+
parameters: {
1607+
nodes: [
1608+
{
1609+
value: {
1610+
__typename: "LiteralValue",
1611+
value: true
1612+
}
1613+
},
1614+
{
1615+
value: {
1616+
__typename: "NodeFunctionIdWrapper",
1617+
id: "gid://sagittarius/NodeFunction/2"
1618+
}
1619+
}
1620+
]
1621+
},
1622+
},
1623+
{
1624+
id: "gid://sagittarius/NodeFunction/2",
1625+
functionDefinition: {identifier: "std::number::add"},
1626+
parameters: {
1627+
nodes: [
1628+
{value: {__typename: "LiteralValue", value: 0}},
1629+
{value: {__typename: "LiteralValue", value: 0}}
1630+
]
1631+
},
1632+
nextNodeId: "gid://sagittarius/NodeFunction/3",
1633+
},
1634+
{
1635+
id: "gid://sagittarius/NodeFunction/3",
1636+
functionDefinition: {identifier: "std::control::return"},
1637+
parameters: {
1638+
nodes: [
1639+
{
1640+
value: {
1641+
__typename: "ReferenceValue",
1642+
nodeFunctionId: "gid://sagittarius/NodeFunction/2",
1643+
}
1644+
}
1645+
]
1646+
}
1647+
}
1648+
]
1649+
}
1650+
};
1651+
1652+
const result = getFlowValidation(flow, FUNCTION_SIGNATURES, DATA_TYPES);
1653+
1654+
expect(result.isValid).toBe(true);
1655+
result.diagnostics.forEach((error) => {
1656+
expect(error.nodeId).toBeDefined()
1657+
expect(error.parameterIndex).toBeDefined()
1658+
})
1659+
});
1660+
15971661
});

0 commit comments

Comments
 (0)