Files
Nathan Rajlich 5d95abf941 fix(swc-plugin): closure variable detection for new expressions and module-level declarations (#1368)
* fix(swc-plugin): closure variable detection for `new` expressions and module-level declarations

Fix two SWC compiler plugin bugs related to closure variable detection:

1. Add Expr::New handling to ClosureVariableCollector so `new Class(...args)`
   properly captures both the callee and arguments as closure variables.

2. Exclude module-level declarations (functions, variables, classes) from
   closure variable detection, preventing over-capturing of identifiers
   that are already available in all bundles. This also allows DCE to
   properly remove step-only helpers and their imports from the workflow
   bundle.

Fixes #1365

* fix(swc-plugin): handle additional expression/statement types in closure variable collector

Expand closure variable detection to cover more AST patterns:

Expressions: Seq (comma), Yield, OptChain, Prop::Shorthand,
computed property keys, Prop::Assign defaults, Class (skip bodies)

Statements: Throw, Try/Catch/Finally, Switch, ForIn, ForOf,
DoWhile, Labeled

Also fix existing Prop::Shorthand bug where object shorthand
properties like { url } were not being collected as closure vars.

Extend test fixture with cases for all newly handled patterns.
Fix spec.md wording per review feedback.

* fix(swc-plugin): preserve original step function bodies in enclosing functions

In step mode, nested step functions were replaced with bare references
to the hoisted copy (e.g., `return hoisted$fn;`). This broke direct
calls because the hoisted copy uses `__private_getClosureVars()` which
only works in workflow context.

Now the original function body is preserved inline with just the
directive stripped, so JavaScript's normal closure semantics work for
direct calls. The hoisted copy with `__private_getClosureVars()` is
still registered for workflow-driven execution.

Fixes #1369

* fix(swc-plugin): restore metadata tracking for object property steps in step mode

The previous commit accidentally removed the
object_property_workflow_conversions tracking from the step mode path,
causing __internal_workflows metadata to be stripped from step bundle
output for object property step functions.

* fix(swc-plugin): detect closure variables inside nested function/method bodies

The closure variable collector was skipping nested function expressions,
arrow functions, and method bodies entirely. This meant closure
variables used deep inside inner functions (e.g., a variable used inside
a ReadableStream's start() method) were not captured.

Now the collector walks into nested function/arrow/method/getter/setter
bodies while adding their parameters to the local var set, so only
truly free variables from the outer step scope are captured.

Also add ReadableStream, WritableStream, TransformStream, and other
common Web API globals to the known globals list.

* update changeset to include Bug 4

* fix(swc-plugin): handle TypeScript expression wrappers and class bodies in closure detection

After comparing with Next.js's SWC plugin closure detection approach,
identified and fixed remaining gaps:

- TypeScript expression wrappers (as, satisfies, !, type assertions,
  const assertions, instantiation expressions) now traverse to the
  inner expression instead of being silently skipped
- Class expressions and declarations now walk their body members
  (methods, properties, constructors, static blocks) to detect
  closure variables used inside them
- Document all remaining safe-to-skip Expr variants (This, Lit,
  SuperProp, MetaProp, PrivateName, Invalid, JSX)

* test: add fixture cases for TypeScript wrappers and class body closure detection

* test: add TypeScript fixture for closure detection through TS expression wrappers

Add a proper input.ts fixture that tests closure variable detection
through real TypeScript syntax: `as`, `satisfies`, `!` (non-null),
angle-bracket type assertions, `as const`, and generic function calls.

Update test harness to support input.ts files by adding swc_ecma_parser
dev-dependency and auto-detecting TypeScript syntax from file extension.

Remove the incorrectly placed TypeScript-related test cases from the JS
fixture (they were using plain JS syntax, not actual TS wrappers).
2026-03-16 23:04:36 +00:00

20 lines
585 B
TOML

[package]
authors = { workspace = true }
description = "AST Transforms for workflow"
edition = { workspace = true }
# homepage = { workspace = true }
license = { workspace = true }
name = "swc_workflow"
# repository = { workspace = true }
rust-version = { workspace = true }
version = "0.1.0"
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
swc_core = { workspace = true, features = ["ecma_plugin_transform"] }
[dev-dependencies]
swc_ecma_parser = { workspace = true }
testing = { workspace = true }