Skip to content

Commit 959fc02

Browse files
committed
.
1 parent ef73dc7 commit 959fc02

File tree

5 files changed

+219
-16
lines changed

5 files changed

+219
-16
lines changed

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

Lines changed: 218 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,20 +4122,63 @@ impl VisitMut for StepTransform {
41224122

41234123
fn visit_mut_export_decl(&mut self, export_decl: &mut ExportDecl) {
41244124
// Check if this is a workflow function first, to set in_workflow_function flag
4125-
let is_workflow_function = if let Decl::Fn(fn_decl) = &export_decl.decl {
4126-
let fn_name = fn_decl.ident.sym.to_string();
4127-
self.workflow_function_names.contains(&fn_name)
4128-
|| self.has_workflow_directive(&fn_decl.function, true)
4129-
} else {
4130-
false
4125+
let is_workflow_function = match &export_decl.decl {
4126+
Decl::Fn(fn_decl) => {
4127+
let fn_name = fn_decl.ident.sym.to_string();
4128+
self.workflow_function_names.contains(&fn_name)
4129+
|| self.has_workflow_directive(&fn_decl.function, true)
4130+
}
4131+
Decl::Var(var_decl) => {
4132+
// Check if any variables are workflow functions
4133+
var_decl.decls.iter().any(|decl| {
4134+
if let Pat::Ident(binding) = &decl.name {
4135+
let name = binding.id.sym.to_string();
4136+
if self.workflow_function_names.contains(&name) {
4137+
return true;
4138+
}
4139+
// Also check if the initializer has a workflow directive
4140+
if let Some(init) = &decl.init {
4141+
match &**init {
4142+
Expr::Arrow(arrow) => {
4143+
self.has_workflow_directive_arrow(arrow, true)
4144+
}
4145+
Expr::Fn(fn_expr) => {
4146+
self.has_workflow_directive(&fn_expr.function, true)
4147+
}
4148+
_ => false,
4149+
}
4150+
} else {
4151+
false
4152+
}
4153+
} else {
4154+
false
4155+
}
4156+
})
4157+
}
4158+
_ => false,
41314159
};
41324160

41334161
#[cfg(debug_assertions)]
4134-
if let Decl::Fn(fn_decl) = &export_decl.decl {
4135-
eprintln!(
4136-
"export fn {} workflow? {} (mode={:?})",
4137-
fn_decl.ident.sym, is_workflow_function, self.mode
4138-
);
4162+
{
4163+
match &export_decl.decl {
4164+
Decl::Fn(fn_decl) => {
4165+
eprintln!(
4166+
"export fn {} workflow? {} (mode={:?})",
4167+
fn_decl.ident.sym, is_workflow_function, self.mode
4168+
);
4169+
}
4170+
Decl::Var(var_decl) => {
4171+
for decl in &var_decl.decls {
4172+
if let Pat::Ident(binding) = &decl.name {
4173+
eprintln!(
4174+
"export var {} workflow? {} (mode={:?})",
4175+
binding.id.sym, is_workflow_function, self.mode
4176+
);
4177+
}
4178+
}
4179+
}
4180+
_ => {}
4181+
}
41394182
}
41404183

41414184
let old_in_workflow = self.in_workflow_function;
@@ -4343,6 +4386,86 @@ impl VisitMut for StepTransform {
43434386
self.remove_use_workflow_directive(&mut fn_decl.function.body);
43444387
}
43454388
}
4389+
} else if let Decl::Var(var_decl) = &mut export_decl.decl {
4390+
// In Step mode, replace exported workflow arrow function bodies with throw after processing nested steps
4391+
if matches!(self.mode, TransformMode::Step) {
4392+
for decl in var_decl.decls.iter_mut() {
4393+
if let Some(init) = &mut decl.init {
4394+
if let Pat::Ident(binding) = &decl.name {
4395+
let name = binding.id.sym.to_string();
4396+
if self.workflow_function_names.contains(&name) {
4397+
if let Expr::Arrow(arrow_expr) = &mut **init {
4398+
// Replace body with throw error
4399+
let error_msg = format!(
4400+
"You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api",
4401+
name, name
4402+
);
4403+
let error_expr = Expr::New(NewExpr {
4404+
span: DUMMY_SP,
4405+
ctxt: SyntaxContext::empty(),
4406+
callee: Box::new(Expr::Ident(Ident::new(
4407+
"Error".into(),
4408+
DUMMY_SP,
4409+
SyntaxContext::empty(),
4410+
))),
4411+
args: Some(vec![ExprOrSpread {
4412+
spread: None,
4413+
expr: Box::new(Expr::Lit(Lit::Str(Str {
4414+
span: DUMMY_SP,
4415+
value: error_msg.into(),
4416+
raw: None,
4417+
}))),
4418+
}]),
4419+
type_args: None,
4420+
});
4421+
arrow_expr.body = Box::new(
4422+
BlockStmtOrExpr::BlockStmt(BlockStmt {
4423+
span: DUMMY_SP,
4424+
ctxt: SyntaxContext::empty(),
4425+
stmts: vec![Stmt::Throw(ThrowStmt {
4426+
span: DUMMY_SP,
4427+
arg: Box::new(error_expr),
4428+
})],
4429+
}),
4430+
);
4431+
} else if let Expr::Fn(fn_expr) = &mut **init {
4432+
// Replace body with throw error
4433+
let error_msg = format!(
4434+
"You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api",
4435+
name, name
4436+
);
4437+
if let Some(body) = &mut fn_expr.function.body {
4438+
let error_expr = Expr::New(NewExpr {
4439+
span: DUMMY_SP,
4440+
ctxt: SyntaxContext::empty(),
4441+
callee: Box::new(Expr::Ident(Ident::new(
4442+
"Error".into(),
4443+
DUMMY_SP,
4444+
SyntaxContext::empty(),
4445+
))),
4446+
args: Some(vec![ExprOrSpread {
4447+
spread: None,
4448+
expr: Box::new(Expr::Lit(Lit::Str(
4449+
Str {
4450+
span: DUMMY_SP,
4451+
value: error_msg.into(),
4452+
raw: None,
4453+
},
4454+
))),
4455+
}]),
4456+
type_args: None,
4457+
});
4458+
body.stmts = vec![Stmt::Throw(ThrowStmt {
4459+
span: DUMMY_SP,
4460+
arg: Box::new(error_expr),
4461+
})];
4462+
}
4463+
}
4464+
}
4465+
}
4466+
}
4467+
}
4468+
}
43464469
}
43474470
} else {
43484471
export_decl.visit_mut_children_with(self);
@@ -4540,7 +4663,13 @@ impl VisitMut for StepTransform {
45404663

45414664
match self.mode {
45424665
TransformMode::Step => {
4543-
// Workflow functions are not processed in step mode
4666+
// In step mode, remove directive but keep original body for now
4667+
// We need to visit children first to process any nested steps
4668+
self.remove_use_workflow_directive_arrow(
4669+
&mut arrow_expr.body,
4670+
);
4671+
self.workflow_functions_needing_id
4672+
.push((name.clone(), arrow_expr.span));
45444673
}
45454674
TransformMode::Workflow => {
45464675
// In workflow mode, just remove the directive
@@ -4663,6 +4792,83 @@ impl VisitMut for StepTransform {
46634792
}
46644793
}
46654794
}
4795+
4796+
// After processing directives, replace workflow bodies in step mode
4797+
if matches!(self.mode, TransformMode::Step) {
4798+
for decl in var_decl.decls.iter_mut() {
4799+
if let Some(init) = &mut decl.init {
4800+
if let Pat::Ident(binding) = &decl.name {
4801+
let name = binding.id.sym.to_string();
4802+
if self.workflow_function_names.contains(&name) {
4803+
if let Expr::Arrow(arrow_expr) = &mut **init {
4804+
// Replace body with throw error
4805+
let error_msg = format!(
4806+
"You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api",
4807+
name, name
4808+
);
4809+
let error_expr = Expr::New(NewExpr {
4810+
span: DUMMY_SP,
4811+
ctxt: SyntaxContext::empty(),
4812+
callee: Box::new(Expr::Ident(Ident::new(
4813+
"Error".into(),
4814+
DUMMY_SP,
4815+
SyntaxContext::empty(),
4816+
))),
4817+
args: Some(vec![ExprOrSpread {
4818+
spread: None,
4819+
expr: Box::new(Expr::Lit(Lit::Str(Str {
4820+
span: DUMMY_SP,
4821+
value: error_msg.into(),
4822+
raw: None,
4823+
}))),
4824+
}]),
4825+
type_args: None,
4826+
});
4827+
arrow_expr.body =
4828+
Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
4829+
span: DUMMY_SP,
4830+
ctxt: SyntaxContext::empty(),
4831+
stmts: vec![Stmt::Throw(ThrowStmt {
4832+
span: DUMMY_SP,
4833+
arg: Box::new(error_expr),
4834+
})],
4835+
}));
4836+
} else if let Expr::Fn(fn_expr) = &mut **init {
4837+
// Replace body with throw error
4838+
let error_msg = format!(
4839+
"You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api",
4840+
name, name
4841+
);
4842+
if let Some(body) = &mut fn_expr.function.body {
4843+
let error_expr = Expr::New(NewExpr {
4844+
span: DUMMY_SP,
4845+
ctxt: SyntaxContext::empty(),
4846+
callee: Box::new(Expr::Ident(Ident::new(
4847+
"Error".into(),
4848+
DUMMY_SP,
4849+
SyntaxContext::empty(),
4850+
))),
4851+
args: Some(vec![ExprOrSpread {
4852+
spread: None,
4853+
expr: Box::new(Expr::Lit(Lit::Str(Str {
4854+
span: DUMMY_SP,
4855+
value: error_msg.into(),
4856+
raw: None,
4857+
}))),
4858+
}]),
4859+
type_args: None,
4860+
});
4861+
body.stmts = vec![Stmt::Throw(ThrowStmt {
4862+
span: DUMMY_SP,
4863+
arg: Box::new(error_expr),
4864+
})];
4865+
}
4866+
}
4867+
}
4868+
}
4869+
}
4870+
}
4871+
}
46664872
}
46674873
_ => {}
46684874
}

packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-client.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export function syncStep() {
66
}
77
// Error: sync arrow function with use workflow
88
export const syncWorkflow = ()=>{
9-
'use workflow';
109
return 'test';
1110
};
1211
// These are ok

packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-step.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export function syncStep() {
77
}
88
// Error: sync arrow function with use workflow
99
export const syncWorkflow = ()=>{
10-
'use workflow';
1110
return 'test';
1211
};
1312
// Error: sync method with use step

packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-workflow.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export function syncStep() {
66
}
77
// Error: sync arrow function with use workflow
88
export const syncWorkflow = ()=>{
9-
'use workflow';
109
return 'test';
1110
};
1211
// These are ok

packages/swc-plugin-workflow/transform/tests/fixture/module-level-workflow/output-step.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ export async function workflow(input) {
1111
}
1212
workflow.workflowId = "workflow//input.js//workflow";
1313
export const arrowWorkflow = async (input)=>{
14-
return input.bar;
14+
throw new Error("You attempted to execute workflow arrowWorkflow function directly. To start a workflow, use start(arrowWorkflow) from workflow/api");
1515
};
1616
arrowWorkflow.workflowId = "workflow//input.js//arrowWorkflow";

0 commit comments

Comments
 (0)