Skip to content

Commit da37349

Browse files
[swc-plugin] Fix eager discovery for object property steps (#2484)
* Fix eager discovery for object property steps * Add changeset for object property step discovery
1 parent 2599da0 commit da37349

4 files changed

Lines changed: 120 additions & 18 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/swc-plugin": patch
3+
---
4+
5+
Fix detect mode discovery for object-property step handlers so eager builds register steps declared in callback objects.

‎packages/swc-plugin-workflow/transform/src/lib.rs‎

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,27 @@ impl StepTransform {
21562156
naming::format_name(prefix, &self.get_module_path(), &fn_name)
21572157
}
21582158

2159+
fn record_object_property_step_metadata(&mut self, parent_var_name: &str, prop_key: &str) {
2160+
let step_id = self.create_object_property_id(
2161+
parent_var_name,
2162+
prop_key,
2163+
false,
2164+
self.current_workflow_function_name.as_deref(),
2165+
);
2166+
if self.object_property_workflow_conversions.iter().any(
2167+
|(parent, prop, existing_step_id)| {
2168+
parent == parent_var_name && prop == prop_key && existing_step_id == &step_id
2169+
},
2170+
) {
2171+
return;
2172+
}
2173+
self.object_property_workflow_conversions.push((
2174+
parent_var_name.to_string(),
2175+
prop_key.to_string(),
2176+
step_id,
2177+
));
2178+
}
2179+
21592180
// Process object properties for step functions
21602181
fn process_object_properties_for_step_functions(
21612182
&mut self,
@@ -2185,6 +2206,14 @@ impl StepTransform {
21852206
};
21862207

21872208
if should_transform {
2209+
if matches!(self.mode, TransformMode::Detect) {
2210+
self.record_object_property_step_metadata(
2211+
parent_var_name,
2212+
&prop_key,
2213+
);
2214+
continue;
2215+
}
2216+
21882217
// Process the transformation
21892218
match &mut *kv_prop.value {
21902219
Expr::Arrow(arrow_expr) => {
@@ -2316,6 +2345,14 @@ impl StepTransform {
23162345
};
23172346

23182347
if self.has_use_step_directive(&method_prop.function.body) {
2348+
if matches!(self.mode, TransformMode::Detect) {
2349+
self.record_object_property_step_metadata(
2350+
parent_var_name,
2351+
&prop_key,
2352+
);
2353+
continue;
2354+
}
2355+
23192356
// Remove the directive first
23202357
self.remove_use_step_directive(&mut method_prop.function.body);
23212358

@@ -2423,6 +2460,14 @@ impl StepTransform {
24232460
.emit()
24242461
});
24252462
} else if has_step {
2463+
if matches!(self.mode, TransformMode::Detect) {
2464+
self.record_object_property_step_metadata(
2465+
parent_var_name,
2466+
&prop_key,
2467+
);
2468+
continue;
2469+
}
2470+
24262471
// Getters don't need async validation (they can't be async syntactically)
24272472

24282473
// Remove the directive from the getter body
@@ -2551,13 +2596,6 @@ impl StepTransform {
25512596
prop_key: &str,
25522597
_span: swc_core::common::Span,
25532598
) {
2554-
let step_id = self.create_object_property_id(
2555-
parent_var_name,
2556-
prop_key,
2557-
false,
2558-
self.current_workflow_function_name.as_deref(),
2559-
);
2560-
25612599
match self.mode {
25622600
TransformMode::Step => {
25632601
// Replace with reference to hoisted variable so the stepId
@@ -2575,22 +2613,22 @@ impl StepTransform {
25752613
SyntaxContext::empty(),
25762614
));
25772615
// Track for metadata generation
2578-
self.object_property_workflow_conversions.push((
2579-
parent_var_name.to_string(),
2580-
prop_key.to_string(),
2581-
step_id,
2582-
));
2616+
self.record_object_property_step_metadata(parent_var_name, prop_key);
25832617
}
25842618
TransformMode::Workflow => {
2619+
let step_id = self.create_object_property_id(
2620+
parent_var_name,
2621+
prop_key,
2622+
false,
2623+
self.current_workflow_function_name.as_deref(),
2624+
);
25852625
// Replace with initializer call
25862626
*kv_prop.value = self.create_step_initializer(&step_id);
2587-
self.object_property_workflow_conversions.push((
2588-
parent_var_name.to_string(),
2589-
prop_key.to_string(),
2590-
step_id,
2591-
));
2627+
self.record_object_property_step_metadata(parent_var_name, prop_key);
2628+
}
2629+
TransformMode::Detect => {
2630+
self.record_object_property_step_metadata(parent_var_name, prop_key);
25922631
}
2593-
TransformMode::Detect => {}
25942632
}
25952633
}
25962634

‎packages/swc-plugin-workflow/transform/tests/fixture.rs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,24 @@ fn workflow_mode(input: PathBuf) {
5656
},
5757
);
5858
}
59+
60+
#[testing::fixture("tests/fixture/object-property-step/input.js")]
61+
fn detect_mode_object_property_step(input: PathBuf) {
62+
let detect_output = input.parent().unwrap().join("output-detect.js");
63+
test_fixture(
64+
syntax_for(&input),
65+
&|_| {
66+
visit_mut_pass(StepTransform::new(
67+
TransformMode::Detect,
68+
input.file_name().unwrap().to_string_lossy().to_string(),
69+
None,
70+
))
71+
},
72+
&input,
73+
&detect_output,
74+
FixtureTestConfig {
75+
module: Some(true),
76+
..Default::default()
77+
},
78+
);
79+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as z from 'zod';
2+
import { tool } from 'ai';
3+
/**__internal_workflows{"steps":{"input.js":{"timeTool/execute":{"stepId":"step//./input//timeTool/execute"},"weatherTool/execute":{"stepId":"step//./input//weatherTool/execute"},"weatherTool2/execute":{"stepId":"step//./input//weatherTool2/execute"}}}}*/;
4+
export const weatherTool = tool({
5+
description: 'Get the weather in a location',
6+
inputSchema: z.object({
7+
location: z.string().describe('The location to get the weather for')
8+
}),
9+
execute: async ({ location })=>{
10+
"use step";
11+
return {
12+
location,
13+
temperature: 72 + Math.floor(Math.random() * 21) - 10
14+
};
15+
}
16+
});
17+
export const timeTool = tool({
18+
description: 'Get the current time',
19+
execute: async function timeToolImpl() {
20+
"use step";
21+
return {
22+
time: new Date().toISOString()
23+
};
24+
}
25+
});
26+
export const weatherTool2 = tool({
27+
description: 'Get the weather in a location',
28+
inputSchema: z.object({
29+
location: z.string().describe('The location to get the weather for')
30+
}),
31+
async execute ({ location }) {
32+
"use step";
33+
return {
34+
location,
35+
temperature: 72 + Math.floor(Math.random() * 21) - 10
36+
};
37+
}
38+
});

0 commit comments

Comments
 (0)