From eb71c41e9340d46ddf4f74b3622caaf404e4c3f5 Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Fri, 6 Jun 2025 12:49:11 +0200 Subject: [PATCH] fix(editor): In Sub-workflow Conversion handle standalone accessor and accessor[0] cases correctly (#16086) --- .../src/node-reference-parser-utils.ts | 7 +++++-- .../test/node-reference-parser-utils.test.ts | 21 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/workflow/src/node-reference-parser-utils.ts b/packages/workflow/src/node-reference-parser-utils.ts index 9a8388a614..800d20599a 100644 --- a/packages/workflow/src/node-reference-parser-utils.ts +++ b/packages/workflow/src/node-reference-parser-utils.ts @@ -207,7 +207,7 @@ function parseExpressionMapping( return { nodeNameInExpression, originalExpression: `${exprStart}.${parts[0]}`, // $('abc').first() - replacementPrefix: `$('${startNodeName}').${accessorPrefix}`, // $('Start').first() + replacementPrefix: `$('${startNodeName}').${accessorPrefix}.json`, // $('Start').first().json replacementName: `${nodeNamePlainJs}_${convertDataAccessorName(originalName)}`, // nodeName_firstItem, nodeName_itemMatching_20 }; } else { @@ -266,7 +266,9 @@ function extractExpressionCandidate(expression: string, startIndex: number, endI // Note that by choosing match 0 we use `itemMatching` matches over `item` // matches by relying on the order in ITEM_TO_DATA_ACCESSORS - const after_accessor_idx = endIndex + (firstPartException[0]?.[0].length ?? -1) + 1; + let after_accessor_idx = endIndex + (firstPartException[0]?.[0].length ?? -1); + // skip `.` to continue, but halt before other symbols like `[` in `all()[0]` + if (expression[after_accessor_idx + 1] === '.') after_accessor_idx += 1; const after_accessor = expression.slice(after_accessor_idx); const firstInvalidCharMatch = INVALID_JS_DOT_PATH.exec(after_accessor); @@ -296,6 +298,7 @@ function parseCandidateMatch( const candidate = extractExpressionCandidate(expression, startIndex, endIndex); if (candidate === null) return null; + return parseExpressionMapping( candidate, nodeNameInExpression, diff --git a/packages/workflow/test/node-reference-parser-utils.test.ts b/packages/workflow/test/node-reference-parser-utils.test.ts index f30b61ca64..f65c5eacb1 100644 --- a/packages/workflow/test/node-reference-parser-utils.test.ts +++ b/packages/workflow/test/node-reference-parser-utils.test.ts @@ -508,6 +508,8 @@ describe('NodeReferenceParserUtils', () => { '$("A").last().json.myField', '$("A").all().json.myField', '$("A").item.json.myField', + '$("A").first()', + '$("A").all()', ]), ]; nodeNames = ['A', 'B']; @@ -517,6 +519,8 @@ describe('NodeReferenceParserUtils', () => { ['myField_lastItem', '$("A").last().json.myField'], ['myField_allItems', '$("A").all().json.myField'], ['myField', '$("A").item.json.myField'], + ['A_firstItem', '$("A").first()'], + ['A_allItems', '$("A").all()'], ]); expect(result.nodes).toEqual([ { @@ -526,6 +530,8 @@ describe('NodeReferenceParserUtils', () => { p1: "={{ $('Start').last().json.myField_lastItem }}", p2: "={{ $('Start').first().json.myField_allItems }}", p3: "={{ $('Start').item.json.myField }}", + p4: "={{ $('Start').first().json.A_firstItem }}", + p5: "={{ $('Start').first().json.A_allItems }}", }, }, ]); @@ -695,7 +701,20 @@ describe('NodeReferenceParserUtils', () => { }, ]); }); - + it('should support handle unexpected code after the data accessor', () => { + nodes = [makeNode('A', ['$("B").all()[0].json.first_node_variable'])]; + nodeNames = ['A', 'B']; + const result = extractReferencesInNodeExpressions(nodes, nodeNames, startNodeName); + expect([...result.variables.entries()]).toEqual([['B_allItems', '$("B").all()']]); + expect(result.nodes).toEqual([ + { + name: 'A', + parameters: { + p0: "={{ $('Start').first().json.B_allItems[0].json.first_node_variable }}", + }, + }, + ]); + }); it('should carry over unrelated properties', () => { nodes = [ {