diff --git a/.changeset/fix-default-export-workflow.md b/.changeset/fix-default-export-workflow.md new file mode 100644 index 000000000..f0b90e4f9 --- /dev/null +++ b/.changeset/fix-default-export-workflow.md @@ -0,0 +1,5 @@ +--- +"@workflow/swc-plugin": patch +--- + +Fix default export workflow function transformation in workflow mode diff --git a/packages/swc-plugin-workflow/transform/src/lib.rs b/packages/swc-plugin-workflow/transform/src/lib.rs index 07a8df0d8..ac490f47a 100644 --- a/packages/swc-plugin-workflow/transform/src/lib.rs +++ b/packages/swc-plugin-workflow/transform/src/lib.rs @@ -1,7 +1,7 @@ mod naming; use serde::Deserialize; -use std::collections::HashSet; +use std::collections::{HashSet, HashMap}; use swc_core::{ common::{DUMMY_SP, SyntaxContext, errors::HANDLER}, ecma::{ @@ -158,6 +158,8 @@ pub struct StepTransform { step_function_names: HashSet, // Set of function names that are workflow functions workflow_function_names: HashSet, + // Map from export name to actual const name for default exports (e.g., "default" -> "__default") + workflow_export_to_const_name: std::collections::HashMap, // Set of function names that have been registered (to avoid duplicates) registered_functions: HashSet, // Collect registration calls for step mode @@ -178,6 +180,10 @@ pub struct StepTransform { step_exports_to_convert: Vec<(String, String, swc_core::common::Span)>, // (fn_name, step_id, span) // Track default exports that need to be replaced with expressions default_exports_to_replace: Vec<(String, Expr)>, // (export_name, replacement_expr) + // Track default workflow exports that need const declarations in workflow mode + default_workflow_exports: Vec<(String, Expr, swc_core::common::Span)>, // (const_name, expr, span) + // Track all declared identifiers in module scope to avoid collisions + declared_identifiers: HashSet, // Track object property step functions for hoisting in step mode // (parent_var_name, prop_name, arrow_expr, span) object_property_step_functions: Vec<(String, String, ArrowExpr, swc_core::common::Span)>, @@ -441,6 +447,7 @@ impl StepTransform { has_file_workflow_directive: false, step_function_names: HashSet::new(), workflow_function_names: HashSet::new(), + workflow_export_to_const_name: HashMap::new(), registered_functions: HashSet::new(), registration_calls: Vec::new(), names: Vec::new(), @@ -453,6 +460,8 @@ impl StepTransform { workflow_functions_needing_id: Vec::new(), step_exports_to_convert: Vec::new(), default_exports_to_replace: Vec::new(), + default_workflow_exports: Vec::new(), + declared_identifiers: HashSet::new(), object_property_step_functions: Vec::new(), nested_step_functions: Vec::new(), anonymous_fn_counter: 0, @@ -485,6 +494,133 @@ impl StepTransform { } } + // Generate a unique identifier that doesn't conflict with existing declarations + fn generate_unique_name(&self, base_name: &str) -> String { + let mut name = base_name.to_string(); + let mut counter = 0; + + while self.declared_identifiers.contains(&name) { + counter += 1; + name = format!("{}${}", base_name, counter); + } + + name + } + + // Collect all declared identifiers in the module to avoid naming collisions + fn collect_declared_identifiers(&mut self, items: &[ModuleItem]) { + for item in items { + match item { + ModuleItem::Stmt(Stmt::Decl(decl)) => { + match decl { + Decl::Fn(fn_decl) => { + self.declared_identifiers.insert(fn_decl.ident.sym.to_string()); + } + Decl::Var(var_decl) => { + for declarator in &var_decl.decls { + self.collect_idents_from_pat(&declarator.name); + } + } + Decl::Class(class_decl) => { + self.declared_identifiers.insert(class_decl.ident.sym.to_string()); + } + _ => {} + } + } + ModuleItem::ModuleDecl(module_decl) => { + match module_decl { + ModuleDecl::ExportDecl(export_decl) => { + match &export_decl.decl { + Decl::Fn(fn_decl) => { + self.declared_identifiers.insert(fn_decl.ident.sym.to_string()); + } + Decl::Var(var_decl) => { + for declarator in &var_decl.decls { + self.collect_idents_from_pat(&declarator.name); + } + } + Decl::Class(class_decl) => { + self.declared_identifiers.insert(class_decl.ident.sym.to_string()); + } + _ => {} + } + } + ModuleDecl::ExportDefaultDecl(default_decl) => { + match &default_decl.decl { + DefaultDecl::Fn(fn_expr) => { + if let Some(ident) = &fn_expr.ident { + self.declared_identifiers.insert(ident.sym.to_string()); + } + } + DefaultDecl::Class(class_expr) => { + if let Some(ident) = &class_expr.ident { + self.declared_identifiers.insert(ident.sym.to_string()); + } + } + _ => {} + } + } + ModuleDecl::Import(import_decl) => { + for specifier in &import_decl.specifiers { + match specifier { + ImportSpecifier::Named(named) => { + self.declared_identifiers.insert(named.local.sym.to_string()); + } + ImportSpecifier::Default(default) => { + self.declared_identifiers.insert(default.local.sym.to_string()); + } + ImportSpecifier::Namespace(namespace) => { + self.declared_identifiers.insert(namespace.local.sym.to_string()); + } + } + } + } + _ => {} + } + } + _ => {} + } + } + } + + // Helper to collect identifiers from patterns (for destructuring, etc.) + fn collect_idents_from_pat(&mut self, pat: &Pat) { + match pat { + Pat::Ident(ident) => { + self.declared_identifiers.insert(ident.id.sym.to_string()); + } + Pat::Array(array_pat) => { + for elem in &array_pat.elems { + if let Some(elem) = elem { + self.collect_idents_from_pat(elem); + } + } + } + Pat::Object(obj_pat) => { + for prop in &obj_pat.props { + match prop { + ObjectPatProp::KeyValue(kv) => { + self.collect_idents_from_pat(&kv.value); + } + ObjectPatProp::Assign(assign) => { + self.declared_identifiers.insert(assign.key.sym.to_string()); + } + ObjectPatProp::Rest(rest) => { + self.collect_idents_from_pat(&rest.arg); + } + } + } + } + Pat::Rest(rest_pat) => { + self.collect_idents_from_pat(&rest_pat.arg); + } + Pat::Assign(assign_pat) => { + self.collect_idents_from_pat(&assign_pat.left); + } + _ => {} + } + } + // Create an identifier for an object property step function // Used for functions defined as object properties, e.g., tool({ execute: async () => {...} }) fn create_object_property_id( @@ -1819,8 +1955,14 @@ impl StepTransform { let workflow_entries: Vec = sorted_workflow_names .into_iter() .map(|fn_name| { - let workflow_id = self.create_id(Some(fn_name), DUMMY_SP, true); - format!("\"{}\":{{\"workflowId\":\"{}\"}}", fn_name, workflow_id) + // Check if this export name has a different const name (e.g., "default" -> "__default") + let fn_name_str: &str = fn_name; + let actual_name = self.workflow_export_to_const_name + .get(fn_name_str) + .map(|s| s.as_str()) + .unwrap_or(fn_name_str); + let workflow_id = self.create_id(Some(actual_name), DUMMY_SP, true); + format!("\"{}\":{{\"workflowId\":\"{}\"}}", fn_name_str, workflow_id) }) .collect(); @@ -2442,6 +2584,9 @@ impl VisitMut for StepTransform { } fn visit_mut_module_items(&mut self, items: &mut Vec) { + // Collect all declared identifiers to avoid naming collisions + self.collect_declared_identifiers(items); + // Check for file-level directives self.has_file_step_directive = self.check_module_directive(items); self.has_file_workflow_directive = self.check_module_workflow_directive(items); @@ -2698,9 +2843,13 @@ impl VisitMut for StepTransform { } ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(default_decl)) => { if let DefaultDecl::Fn(fn_expr) = &default_decl.decl { - if let Some(ident) = &fn_expr.ident { - let fn_name = ident.sym.to_string(); - if self.workflow_function_names.contains(&fn_name) { + // Check if this is a workflow function by checking for "default" key + if self.workflow_function_names.contains("default") { + // Only add workflowId for named default exports + // Anonymous ones are handled by default_workflow_exports + if let Some(ident) = &fn_expr.ident { + // Named default export: use the function name + let fn_name = ident.sym.to_string(); items_to_insert.push(( i + 1, ModuleItem::Stmt(self.create_workflow_id_assignment( @@ -2709,18 +2858,7 @@ impl VisitMut for StepTransform { )), )); } - } else { - // Default export without name, use "defaultWorkflow" - let fn_name = "defaultWorkflow"; - if self.workflow_function_names.contains(fn_name) { - items_to_insert.push(( - i + 1, - ModuleItem::Stmt(self.create_workflow_id_assignment( - fn_name, - fn_expr.function.span, - )), - )); - } + // Anonymous default exports will have workflowId added by default_workflow_exports processing } } } @@ -2781,16 +2919,74 @@ impl VisitMut for StepTransform { } } - // Replace default exports that need to be converted - let default_exports: Vec<_> = self.default_exports_to_replace.drain(..).collect(); - for (export_name, replacement_expr) in default_exports { - for item in items.iter_mut() { - if let ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export_default)) = item - { - if let DefaultDecl::Fn(fn_expr) = &export_default.decl { - if let Some(ident) = &fn_expr.ident { - if ident.sym == export_name { - // Replace with export default expression + // Handle default workflow exports (workflow and client modes) + // We need to: 1) find the export default position, 2) replace it with const declaration, + // 3) add workflowId assignment, 4) add export default at the end + if (self.mode == TransformMode::Workflow || self.mode == TransformMode::Client) + && !self.default_workflow_exports.is_empty() { + let default_workflows: Vec<_> = self.default_workflow_exports.drain(..).collect(); + let default_exports: Vec<_> = self.default_exports_to_replace.drain(..).collect(); + + // Find and remove the original export default, note its position + let mut export_position = None; + for (i, item) in items.iter().enumerate() { + match item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(_)) | + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(_)) => { + export_position = Some(i); + break; + } + _ => {} + } + } + + if let Some(pos) = export_position { + // Remove the original export default + items.remove(pos); + + // Insert in correct order: const, workflowId, export default + for (const_name, fn_expr, span) in default_workflows { + // Insert const declaration at the original export position + items.insert(pos, ModuleItem::Stmt(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + ctxt: SyntaxContext::empty(), + kind: VarDeclKind::Const, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(BindingIdent { + id: Ident::new(const_name.clone().into(), DUMMY_SP, SyntaxContext::empty()), + type_ann: None, + }), + init: Some(Box::new(fn_expr)), + definite: false, + }], + }))))); + + // Insert workflowId assignment after const + items.insert(pos + 1, ModuleItem::Stmt( + self.create_workflow_id_assignment(&const_name, span), + )); + + // Insert export default at the end (after workflowId) + for (_export_name, replacement_expr) in &default_exports { + items.insert(pos + 2, ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr( + ExportDefaultExpr { + span: DUMMY_SP, + expr: Box::new(replacement_expr.clone()), + }, + ))); + } + } + } + } else { + // Handle cases where default exports need to be converted but no const declaration + let default_exports: Vec<_> = self.default_exports_to_replace.drain(..).collect(); + for (export_name, replacement_expr) in default_exports { + for item in items.iter_mut() { + match item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(export_default)) => { + if export_name == "default" { *item = ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr( ExportDefaultExpr { span: export_default.span, @@ -2800,6 +2996,18 @@ impl VisitMut for StepTransform { break; } } + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export_default)) => { + if export_name == "default" { + *item = ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr( + ExportDefaultExpr { + span: export_default.span, + expr: Box::new(replacement_expr.clone()), + }, + )); + break; + } + } + _ => {} } } } @@ -4055,7 +4263,20 @@ impl VisitMut for StepTransform { if self.should_transform_workflow_function(&fn_expr.function, true) { if self.validate_async_function(&fn_expr.function, fn_expr.function.span) { - self.workflow_function_names.insert(fn_name.clone()); + // For ALL default exports, track mapping from "default" to actual const name + let const_name = if fn_name == "default" { + // Anonymous: generate unique name + let unique_name = self.generate_unique_name("__default"); + self.workflow_export_to_const_name.insert("default".to_string(), unique_name.clone()); + unique_name + } else { + // Named: use the function name + self.workflow_export_to_const_name.insert("default".to_string(), fn_name.clone()); + fn_name.clone() + }; + + // Always use "default" as the metadata key for default exports + self.workflow_function_names.insert("default".to_string()); match self.mode { TransformMode::Step => { @@ -4065,31 +4286,44 @@ impl VisitMut for StepTransform { // In workflow mode, just remove the directive self.remove_use_workflow_directive(&mut fn_expr.function.body); - // We'll need to handle the expansion differently for default exports - // For now, just track it - let actual_name = format!( - "$$default{}", - if fn_name == "default" { "Workflow" } else { "" } - ); - self.workflow_exports_to_expand.push(( - actual_name, - Expr::Fn(fn_expr.clone()), - fn_expr.function.span, - )); + if fn_name == "default" { + // Anonymous default export: convert to const declaration + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + const_name.clone(), + Expr::Fn(fn_expr.clone()), + fn_expr.function.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + fn_name.clone(), + Expr::Ident(Ident::new( + const_name.into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + } else { + // Named default export: can reference by name + // export default async function name() { ... } + // name.workflowId = "..."; + self.workflow_exports_to_expand.push(( + const_name, + Expr::Fn(fn_expr.clone()), + fn_expr.function.span, + )); + } } TransformMode::Client => { // In client mode, replace workflow function body with error throw self.remove_use_workflow_directive(&mut fn_expr.function.body); - let actual_name = fn_expr - .ident - .as_ref() - .map(|i| i.sym.to_string()) - .unwrap_or_else(|| "defaultWorkflow".to_string()); + + let error_msg = format!( + "You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api", + const_name, const_name + ); if let Some(body) = &mut fn_expr.function.body { - let error_msg = format!( - "You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api", - actual_name, actual_name - ); let error_expr = Expr::New(NewExpr { span: DUMMY_SP, ctxt: SyntaxContext::empty(), @@ -4113,8 +4347,31 @@ impl VisitMut for StepTransform { arg: Box::new(error_expr), })]; } - self.workflow_functions_needing_id - .push((actual_name, fn_expr.function.span)); + + // For anonymous functions, convert to const declaration so we can assign workflowId + if fn_name == "default" { + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + const_name.clone(), + Expr::Fn(fn_expr.clone()), + fn_expr.function.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + fn_name.clone(), + Expr::Ident(Ident::new( + const_name.clone().into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + // workflowId assignment is handled by default_workflow_exports processing + } else { + // Named function can be referenced directly, just add workflowId + self.workflow_functions_needing_id + .push((const_name, fn_expr.function.span)); + } } } } @@ -4178,6 +4435,219 @@ impl VisitMut for StepTransform { } } + // Handle export default expressions (anonymous functions and arrow functions) + fn visit_mut_export_default_expr(&mut self, expr: &mut ExportDefaultExpr) { + match &mut *expr.expr { + Expr::Fn(fn_expr) => { + // Anonymous function: export default async function() { ... } + if self.should_transform_workflow_function(&fn_expr.function, true) { + if self.validate_async_function(&fn_expr.function, fn_expr.function.span) { + // Generate unique name first so we can use it in workflow_function_names + let unique_name = self.generate_unique_name("__default"); + // For function expression default exports, track mapping from "default" to actual const name + self.workflow_export_to_const_name.insert("default".to_string(), unique_name.clone()); + + // Always use "default" as the metadata key for default exports + self.workflow_function_names.insert("default".to_string()); + + match self.mode { + TransformMode::Step => { + // Workflow functions are not processed in step mode + } + TransformMode::Workflow => { + // In workflow mode, convert to const declaration + self.remove_use_workflow_directive(&mut fn_expr.function.body); + + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + unique_name.clone(), + Expr::Fn(fn_expr.clone()), + fn_expr.function.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + "default".to_string(), + Expr::Ident(Ident::new( + unique_name.into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + } + TransformMode::Client => { + // In client mode, replace workflow function body with error throw + self.remove_use_workflow_directive(&mut fn_expr.function.body); + let error_msg = format!( + "You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api", + unique_name, unique_name + ); + if let Some(body) = &mut fn_expr.function.body { + let error_expr = Expr::New(NewExpr { + span: DUMMY_SP, + ctxt: SyntaxContext::empty(), + callee: Box::new(Expr::Ident(Ident::new( + "Error".into(), + DUMMY_SP, + SyntaxContext::empty(), + ))), + args: Some(vec![ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(Str { + span: DUMMY_SP, + value: error_msg.into(), + raw: None, + }))), + }]), + type_args: None, + }); + body.stmts = vec![Stmt::Throw(ThrowStmt { + span: DUMMY_SP, + arg: Box::new(error_expr), + })]; + } + + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + unique_name.clone(), + Expr::Fn(fn_expr.clone()), + fn_expr.function.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + "default".to_string(), + Expr::Ident(Ident::new( + unique_name.into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + } + } + } + } else if self.should_transform_function(&fn_expr.function, true) { + // Handle step functions + if self.validate_async_function(&fn_expr.function, fn_expr.function.span) { + self.step_function_names.insert("default".to_string()); + // Similar logic for steps... + } + } + } + Expr::Arrow(arrow_expr) => { + // Arrow function: export default async () => { ... } + if self.has_workflow_directive_arrow(arrow_expr, true) { + if !arrow_expr.is_async { + emit_error(WorkflowErrorKind::NonAsyncFunction { + span: arrow_expr.span, + directive: "use workflow", + }); + } else { + // For arrow function default exports, generate unique name and track mapping + let unique_name = self.generate_unique_name("__default"); + self.workflow_export_to_const_name.insert("default".to_string(), unique_name.clone()); + + // Always use "default" as the metadata key for default exports + self.workflow_function_names.insert("default".to_string()); + + match self.mode { + TransformMode::Step => { + // Workflow functions are not processed in step mode + } + TransformMode::Workflow => { + // In workflow mode, convert to const declaration + self.remove_use_workflow_directive_arrow(&mut arrow_expr.body); + + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + unique_name.clone(), + Expr::Arrow(arrow_expr.clone()), + arrow_expr.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + "default".to_string(), + Expr::Ident(Ident::new( + unique_name.into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + } + TransformMode::Client => { + // In client mode, convert to const declaration so we can assign workflowId + self.remove_use_workflow_directive_arrow(&mut arrow_expr.body); + let error_msg = format!( + "You attempted to execute workflow {} function directly. To start a workflow, use start({}) from workflow/api", + unique_name, unique_name + ); + let error_expr = Expr::New(NewExpr { + span: DUMMY_SP, + ctxt: SyntaxContext::empty(), + callee: Box::new(Expr::Ident(Ident::new( + "Error".into(), + DUMMY_SP, + SyntaxContext::empty(), + ))), + args: Some(vec![ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(Str { + span: DUMMY_SP, + value: error_msg.into(), + raw: None, + }))), + }]), + type_args: None, + }); + // Replace arrow body with block containing throw statement + arrow_expr.body = Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt { + span: DUMMY_SP, + ctxt: SyntaxContext::empty(), + stmts: vec![Stmt::Throw(ThrowStmt { + span: DUMMY_SP, + arg: Box::new(error_expr), + })], + })); + + // Track for const declaration and workflowId assignment + self.default_workflow_exports.push(( + unique_name.clone(), + Expr::Arrow(arrow_expr.clone()), + arrow_expr.span, + )); + + // Track for replacement with identifier + self.default_exports_to_replace.push(( + "default".to_string(), + Expr::Ident(Ident::new( + unique_name.into(), + DUMMY_SP, + SyntaxContext::empty(), + )), + )); + } + } + } + } else if self.has_step_directive_arrow(arrow_expr, true) { + // Handle step arrow functions + if !arrow_expr.is_async { + emit_error(WorkflowErrorKind::NonAsyncFunction { + span: arrow_expr.span, + directive: "use step", + }); + } else { + self.step_function_names.insert("default".to_string()); + // Similar logic for steps... + } + } + } + _ => {} + } + + expr.visit_mut_children_with(self); + } + fn visit_mut_module_decl(&mut self, decl: &mut ModuleDecl) { // ExportDecl is fully handled by visit_mut_export_decl, so just delegate // to default visitor which will call visit_mut_export_decl diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/input.js b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/input.js new file mode 100644 index 000000000..717913bb8 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/input.js @@ -0,0 +1,7 @@ +// Test anonymous default export workflow +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} + diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-client.js b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-client.js new file mode 100644 index 000000000..56e4f7406 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-client.js @@ -0,0 +1,7 @@ +// Test anonymous default export workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async function() { + throw new Error("You attempted to execute workflow __default function directly. To start a workflow, use start(__default) from workflow/api"); +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-step.js b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-step.js new file mode 100644 index 000000000..b077c5b14 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-step.js @@ -0,0 +1,7 @@ +// Test anonymous default export workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-workflow.js b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-workflow.js new file mode 100644 index 000000000..aef7b24c0 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/anonymous-default-workflow/output-workflow.js @@ -0,0 +1,8 @@ +// Test anonymous default export workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async function() { + const result = await someStep(); + return result; +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/input.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/input.js new file mode 100644 index 000000000..d4fa519ab --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/input.js @@ -0,0 +1,7 @@ +// Test default export arrow workflow +export default async (data) => { + 'use workflow'; + const processed = await processData(data); + return processed; +}; + diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-client.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-client.js new file mode 100644 index 000000000..8e26bc6cb --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-client.js @@ -0,0 +1,7 @@ +// Test default export arrow workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async (data)=>{ + throw new Error("You attempted to execute workflow __default function directly. To start a workflow, use start(__default) from workflow/api"); +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-step.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-step.js new file mode 100644 index 000000000..e2a524628 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-step.js @@ -0,0 +1,7 @@ +// Test default export arrow workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +export default (async (data)=>{ + 'use workflow'; + const processed = await processData(data); + return processed; +}); diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-workflow.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-workflow.js new file mode 100644 index 000000000..f79827ce0 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-arrow-workflow/output-workflow.js @@ -0,0 +1,8 @@ +// Test default export arrow workflow +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async (data)=>{ + const processed = await processData(data); + return processed; +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/input.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/input.js new file mode 100644 index 000000000..d1f3cca10 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/input.js @@ -0,0 +1,13 @@ +// Existing variable named __default +const __default = "existing variable"; + +// Use it to avoid unused variable +console.log(__default); + +// Anonymous default export should get unique name (__default$1) +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} + diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-client.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-client.js new file mode 100644 index 000000000..bcd09ef34 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-client.js @@ -0,0 +1,10 @@ +// Existing variable named __default +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default$1"}}}}*/; +const __default = "existing variable"; +// Use it to avoid unused variable +console.log(__default); +const __default$1 = async function() { + throw new Error("You attempted to execute workflow __default$1 function directly. To start a workflow, use start(__default$1) from workflow/api"); +}; +__default$1.workflowId = "workflow//input.js//__default$1"; +export default __default$1; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-step.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-step.js new file mode 100644 index 000000000..ef284f7a6 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-step.js @@ -0,0 +1,11 @@ +// Existing variable named __default +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default$1"}}}}*/; +const __default = "existing variable"; +// Use it to avoid unused variable +console.log(__default); +// Anonymous default export should get unique name (__default$1) +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-workflow.js b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-workflow.js new file mode 100644 index 000000000..a384d1b4e --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/default-workflow-collision/output-workflow.js @@ -0,0 +1,11 @@ +// Existing variable named __default +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default$1"}}}}*/; +const __default = "existing variable"; +// Use it to avoid unused variable +console.log(__default); +const __default$1 = async function() { + const result = await someStep(); + return result; +}; +__default$1.workflowId = "workflow//input.js//__default$1"; +export default __default$1; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/input.js b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/input.js new file mode 100644 index 000000000..3792efce3 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/input.js @@ -0,0 +1,5 @@ +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-client.js b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-client.js new file mode 100644 index 000000000..224a09903 --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-client.js @@ -0,0 +1,6 @@ +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async function() { + throw new Error("You attempted to execute workflow __default function directly. To start a workflow, use start(__default) from workflow/api"); +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-step.js b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-step.js new file mode 100644 index 000000000..5a3b5959f --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-step.js @@ -0,0 +1,6 @@ +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +export default async function() { + 'use workflow'; + const result = await someStep(); + return result; +} diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-workflow.js b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-workflow.js new file mode 100644 index 000000000..06a92baef --- /dev/null +++ b/packages/swc-plugin-workflow/transform/tests/fixture/expr-fn-default-workflow/output-workflow.js @@ -0,0 +1,7 @@ +/**__internal_workflows{"workflows":{"input.js":{"default":{"workflowId":"workflow//input.js//__default"}}}}*/; +const __default = async function() { + const result = await someStep(); + return result; +}; +__default.workflowId = "workflow//input.js//__default"; +export default __default; diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-client.js b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-client.js index 32024d916..1c6087fbc 100644 --- a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-client.js +++ b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-client.js @@ -1,5 +1,5 @@ // Test workflow functions in client mode -/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"defaultWorkflow":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; +/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"default":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; export async function myWorkflow() { throw new Error("You attempted to execute workflow myWorkflow function directly. To start a workflow, use start(myWorkflow) from workflow/api"); } diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-step.js b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-step.js index 0feaef5c2..b68c25e65 100644 --- a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-step.js +++ b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-step.js @@ -1,5 +1,5 @@ // Test workflow functions in client mode -/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"defaultWorkflow":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; +/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"default":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; export async function myWorkflow() { 'use workflow'; const result = await someStep(); diff --git a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-workflow.js b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-workflow.js index 1242ca444..50e333a18 100644 --- a/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-workflow.js +++ b/packages/swc-plugin-workflow/transform/tests/fixture/workflow-client-property/output-workflow.js @@ -1,5 +1,5 @@ // Test workflow functions in client mode -/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"defaultWorkflow":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; +/**__internal_workflows{"workflows":{"input.js":{"arrowWorkflow":{"workflowId":"workflow//input.js//arrowWorkflow"},"default":{"workflowId":"workflow//input.js//defaultWorkflow"},"internalWorkflow":{"workflowId":"workflow//input.js//internalWorkflow"},"myWorkflow":{"workflowId":"workflow//input.js//myWorkflow"}}}}*/; export async function myWorkflow() { const result = await someStep(); return result; @@ -23,4 +23,4 @@ export function regularFunction() { } myWorkflow.workflowId = "workflow//input.js//myWorkflow"; arrowWorkflow.workflowId = "workflow//input.js//arrowWorkflow"; -$$default.workflowId = "workflow//input.js//$$default"; +defaultWorkflow.workflowId = "workflow//input.js//defaultWorkflow";