Skip to content

feat: experimental oxc transform#113

Draft
TheAlexLichter wants to merge 9 commits into
unjs:mainfrom
TheAlexLichter:feat/use-oxc
Draft

feat: experimental oxc transform#113
TheAlexLichter wants to merge 9 commits into
unjs:mainfrom
TheAlexLichter:feat/use-oxc

Conversation

@TheAlexLichter

@TheAlexLichter TheAlexLichter commented Aug 14, 2025

Copy link
Copy Markdown
Member

This PR adds a naive plugin and transform based on oxc-parser

Related issue: #111

Summary by CodeRabbit

  • New Features

    • Added support for choosing between two async transform parsers, with the default remaining unchanged.
    • Exposed new transform entry points for both parser options and documented how to enable the experimental one.
    • Expanded the plugin to work with configurable async function and object-based transformation targets.
  • Bug Fixes

    • Improved transform handling across nested scopes, conditional branches, try/catch blocks, and dot-notation call patterns.
    • Added coverage to ensure parser-specific transforms remain isolated and behave consistently.

@codecov

codecov Bot commented Aug 14, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.52941% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.12%. Comparing base (6c14790) to head (831b25d).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/transform/acorn.ts 97.89% 2 Missing ⚠️
src/transform/oxc.ts 98.92% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #113      +/-   ##
==========================================
+ Coverage   93.26%   95.12%   +1.86%     
==========================================
  Files           2        5       +3     
  Lines         193      308     +115     
  Branches       72      115      +43     
==========================================
+ Hits          180      293     +113     
- Misses         13       15       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@TheAlexLichter TheAlexLichter requested a review from pi0 August 14, 2025 10:11
Comment thread package.json Outdated
Comment thread src/plugin-oxc.ts Outdated
Comment thread test/transform-oxc.test.ts Outdated
@TheAlexLichter

Copy link
Copy Markdown
Member Author

Iterating over the first draft, thanks for the feedback 🙏🏻

pi0 and others added 5 commits December 16, 2025 21:14
Resolve the ESM build and transform entry conflicts, keep Oxc dependencies optional, isolate plugin transformer instances, and update the experimental parser integration.
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The transformer is refactored from a single file into a shared contracts module (_shared.ts) plus two backend implementations: the existing Acorn-based transformer (transform/acorn.ts) and a new Oxc-based transformer (transform/oxc.ts). The plugin gains a parser option to lazily select the backend at runtime. Both backends are exported as package subpaths, with oxc-parser and oxc-walker added as optional peer dependencies.

Changes

Optional Oxc Parser Backend

Layer / File(s) Summary
Shared transformer contracts and filter utilities
src/transform/_shared.ts, src/transform.ts
Introduces TransformerOptions, Transformer, kInjected, defaultTransformerOptions, and createTransformerFilter in a new shared module. src/transform.ts is reduced to re-exports from the new submodules.
Acorn transformer backend
src/transform/acorn.ts
Moves the full Acorn-based transformer pipeline into its own module: parses with acorn, walks with estree-walker, rewrites AwaitExpression nodes (including if-consequent specials), injects __executeAsync import and let __temp, __restore; preludes via magic-string.
Oxc transformer backend
src/transform/oxc.ts
New backend using oxc-parser's parseSync and oxc-walker. Implements the same transformation contract: detects async call patterns, rewrites AwaitExpression spans via injectForNode, and prepends the helper import when rewrites occur.
Plugin lazy backend selection
src/plugin.ts
Adds parser?: "acorn" | "oxc" to UnctxPluginOptions. Dynamically imports the selected backend, derives filter via createTransformerFilter, makes the transform handler async, and narrows the unctxPlugin generic to false.
Package exports, peer deps, and build inputs
package.json, build.config.ts
Adds ./transform/acorn and ./transform/oxc subpath exports, marks oxc-parser and oxc-walker as optional peer dependencies, and adds both new source files to the bundle entry inputs.
Tests and documentation
test/plugin.test.ts, test/transform.test.ts, README.md
Adds plugin isolation tests for both backends. Refactors the transform test suite to run all cases against both backends with updated inline snapshots. Documents default Acorn parser and optional Oxc setup in README.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Two parsers now hop side by side,
Acorn and Oxc, both along for the ride.
Shared types and filters in _shared.ts rest,
The plugin picks lazily — dynamic's the best!
Magic strings rewrite each await in the code,
While optional peers lighten the install load. 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding an experimental oxc-based transform.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 142-144: The README install command is adding the optional Oxc
packages as runtime dependencies, but they should be installed as dev
dependencies for the build-time transform. Update the install instruction that
mentions oxc-parser and oxc-walker to use the dev-dependency form, and keep the
guidance aligned with their build-only usage.

In `@src/plugin.ts`:
- Line 37: The transform filter built in createTransformerFilter() is inserting
raw asyncFunctions and objectDefinitions keys into a RegExp, so identifiers
containing regex metacharacters can break matching and skip valid files. Update
createTransformerFilter() to escape each derived name before composing the
transform regex, then keep the filter wiring in plugin.ts using that corrected
filter so filenames with symbols like $, ., or + are matched consistently.

In `@src/transform/_shared.ts`:
- Around line 30-33: The public transformer contract in the shared transform
type is too restrictive because it declares options with force?: false even
though the implementation in the backend transform paths already checks
options_.force to bypass shouldTransform. Update the shared type so transform
accepts force: true as part of the public API, keeping the contract aligned with
the behavior in the transformer entry points and allowing TS consumers to use
the forced path without casts.
- Around line 52-61: The regex in the transformer filter is using configured
names from asyncFunctions and objectDefinitions directly, which can break
matching or throw when names contain regex metacharacters. Update the logic in
the transformer helper that builds the new RegExp so each configured name is
escaped before joining, while keeping the existing asyncFunctions and
objectDefinitions behavior intact. Use the same code path that constructs the
code filter to ensure all configured names are treated as literals.

In `@src/transform/acorn.ts`:
- Around line 61-69: The async wrapper transform in acorn.ts is appending the
sentinel argument too early in the CallExpression handling, so wrappers can get
a new positional arg even when their callback was not rewritten. Update the
logic around transformFunctionArguments(node) and the appendRight(..., ",1")
path to only add the sentinel when that specific callback actually triggered the
__executeAsync rewrite (for both the main wrapper path and the code at the other
referenced block). Use the existing functionName / node / detected flow to gate
the append based on whether the current callback was transformed.
- Around line 118-166: The async function body handling in transformFunctionBody
is assuming every async ArrowFunctionExpression/FunctionExpression has a
BlockStatement, which breaks concise async arrow bodies like async () => await
foo(). Update the logic to detect expression-bodied async arrows before casting
body, and either wrap/convert them into a block or skip the block-only injection
path so the prepend/append edits only target real BlockStatement bodies. Use
transformFunctionBody, body, and the existing injectVariable/appendLeft flow to
ensure the __temp/__restore injection happens inside braces rather than inside
an expression.

In `@src/transform/oxc.ts`:
- Around line 118-166: The async function rewrite in transformFunctionBody
assumes function_.body is always a BlockStatement, but expression-bodied async
arrows like async () => await foo() have an expression body and get corrupted by
the injected variable declaration. Update transformFunctionBody to detect
non-block bodies before casting and rewriting, and either wrap expression-bodied
async arrows into a block or skip this rewrite path; use the function_.body,
injectVariable, and s.appendLeft(body.start + 1, ...) logic as the key places to
adjust.
- Around line 65-73: Gate the extra appended “,1” in
transformCallExpression/transformFunctionArguments logic so it is only added
when that callback actually had an await rewritten, instead of for every
matching asyncFunctions wrapper. Track whether the current CallExpression in
src/transform/oxc.ts was rewritten before calling s.appendRight on the last
argument, and apply the same fix to the corresponding callback handling around
the other referenced block so untouched wrappers keep their original call
signature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ca6199a6-08d7-4098-b12f-76c6bfd27a81

📥 Commits

Reviewing files that changed from the base of the PR and between 7005e81 and 831b25d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • README.md
  • build.config.ts
  • package.json
  • src/plugin.ts
  • src/transform.ts
  • src/transform/_shared.ts
  • src/transform/acorn.ts
  • src/transform/oxc.ts
  • test/plugin.test.ts
  • test/transform.test.ts

Comment thread README.md
Comment on lines +142 to +144
```sh
pnpm add oxc-parser oxc-walker
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Install the optional Oxc packages as dev dependencies.

These packages are only needed for the build-time transform, so pnpm add puts them in runtime dependencies unnecessarily.

Proposed fix
-pnpm add oxc-parser oxc-walker
+pnpm add -D oxc-parser oxc-walker
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```sh
pnpm add oxc-parser oxc-walker
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 142 - 144, The README install command is adding the
optional Oxc packages as runtime dependencies, but they should be installed as
dev dependencies for the build-time transform. Update the install instruction
that mentions oxc-parser and oxc-walker to use the dev-dependency form, and keep
the guidance aligned with their build-only usage.

Comment thread src/plugin.ts
filter: options.transformFilter ?? transformer.filter,
handler(code, id) {
const result = transformer.transform(code);
filter: options.transformFilter ?? createTransformerFilter(options),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Escape helper names before deriving the transform regex.

createTransformerFilter() currently interpolates asyncFunctions and objectDefinitions keys directly into new RegExp(...). Names like use$ctx are valid here, but $, ., +, etc. change the regex and make the plugin skip files that the AST transformer would otherwise handle.

Proposed fix
+function escapeRegex(value: string) {
+  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
 export function createTransformerFilter(options: TransformerOptions): {
   code: RegExp;
 } {
   const {
     asyncFunctions = defaultTransformerOptions().asyncFunctions,
     objectDefinitions = defaultTransformerOptions().objectDefinitions,
   } = options;
   return {
     code: new RegExp(
-      `\\b(${[...asyncFunctions!, ...Object.keys(objectDefinitions!)].join(
-        "|",
-      )})\\(`,
+      `\\b(${[...asyncFunctions!, ...Object.keys(objectDefinitions!)]
+        .map(escapeRegex)
+        .join("|")})\\(`,
     ),
   };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugin.ts` at line 37, The transform filter built in
createTransformerFilter() is inserting raw asyncFunctions and objectDefinitions
keys into a RegExp, so identifiers containing regex metacharacters can break
matching and skip valid files. Update createTransformerFilter() to escape each
derived name before composing the transform regex, then keep the filter wiring
in plugin.ts using that corrected filter so filenames with symbols like $, ., or
+ are matched consistently.

Comment thread src/transform/_shared.ts
Comment on lines +30 to +33
transform: (
code: string,
options?: { force?: false },
) => { code: string; magicString: MagicString } | undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow force: true in the public transformer contract.

The shared type currently declares force?: false, but both backends branch on options_.force to bypass shouldTransform. TS consumers cannot call that path without a cast, so the exported API is stricter than the implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/_shared.ts` around lines 30 - 33, The public transformer
contract in the shared transform type is too restrictive because it declares
options with force?: false even though the implementation in the backend
transform paths already checks options_.force to bypass shouldTransform. Update
the shared type so transform accepts force: true as part of the public API,
keeping the contract aligned with the behavior in the transformer entry points
and allowing TS consumers to use the forced path without casts.

Comment thread src/transform/_shared.ts
Comment on lines +52 to +61
const {
asyncFunctions = defaultTransformerOptions().asyncFunctions,
objectDefinitions = defaultTransformerOptions().objectDefinitions,
} = options;
return {
code: new RegExp(
`\\b(${[...asyncFunctions!, ...Object.keys(objectDefinitions!)].join(
"|",
)})\\(`,
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Escape configured names before building the filter regex.

asyncFunctions and objectDefinitions keys are inserted raw into new RegExp(...). That makes the filter misbehave for valid configured names like foo$, and malformed patterns like a) can make plugin setup throw before any transform runs.

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 56-60: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
\\b(${[...asyncFunctions!, ...Object.keys(objectDefinitions!)].join( "|", )})\\(,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/_shared.ts` around lines 52 - 61, The regex in the transformer
filter is using configured names from asyncFunctions and objectDefinitions
directly, which can break matching or throw when names contain regex
metacharacters. Update the logic in the transformer helper that builds the new
RegExp so each configured name is escaped before joining, while keeping the
existing asyncFunctions and objectDefinitions behavior intact. Use the same code
path that constructs the code filter to ensure all configured names are treated
as literals.

Source: Linters/SAST tools

Comment thread src/transform/acorn.ts
Comment on lines +61 to +69
if (node.type === "CallExpression") {
const functionName = _getFunctionName(node.callee);
if (options.asyncFunctions!.includes(functionName)) {
transformFunctionArguments(node);
if (functionName !== "callAsync") {
const lastArgument = node.arguments[node.arguments.length - 1];
if (lastArgument && lastArgument.loc) {
s.appendRight(toIndex(lastArgument.loc.end), ",1");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Only append the sentinel arg when that specific callback was rewritten.

appendRight(..., ",1") runs for every matched wrapper before you know whether its callback injected any __executeAsync rewrite. If another wrapper in the same file flips detected, callbacks with no await still ship with a new positional arg.

Also applies to: 169-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/acorn.ts` around lines 61 - 69, The async wrapper transform in
acorn.ts is appending the sentinel argument too early in the CallExpression
handling, so wrappers can get a new positional arg even when their callback was
not rewritten. Update the logic around transformFunctionArguments(node) and the
appendRight(..., ",1") path to only add the sentinel when that specific callback
actually triggered the __executeAsync rewrite (for both the main wrapper path
and the code at the other referenced block). Use the existing functionName /
node / detected flow to gate the append based on whether the current callback
was transformed.

Comment thread src/transform/acorn.ts
Comment on lines +118 to +166
function transformFunctionBody(function_: Node) {
if (
function_.type !== "ArrowFunctionExpression" &&
function_.type !== "FunctionExpression"
) {
return;
}

// No need to transform non-async function
if (!function_.async) {
return;
}

const body = function_.body as BlockStatement;

let injectVariable = false;
walk(body, {
enter(
node: MaybeHandledNode,
parent: MaybeHandledNode | undefined | null,
) {
if (node.type === "AwaitExpression" && !node[kInjected]) {
detected = true;
injectVariable = true;
injectForNode(node, parent);
} else if (
node.type === "IfStatement" &&
node.consequent.type === "ExpressionStatement" &&
node.consequent.expression.type === "AwaitExpression"
) {
detected = true;
injectVariable = true;
(node.consequent.expression as MaybeHandledNode)[kInjected] = true;
injectForNode(node.consequent.expression, node);
}
// Skip transform for nested functions
if (
node.type === "ArrowFunctionExpression" ||
node.type === "FunctionExpression" ||
node.type === "FunctionDeclaration"
) {
return this.skip();
}
},
});

if (injectVariable && body.loc) {
s.appendLeft(toIndex(body.loc.start) + 1, "let __temp, __restore;");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Concise async arrow bodies currently transform into invalid syntax.

transformFunctionBody treats every async function body as a BlockStatement, but async () => await foo() has an expression body. The later appendLeft(toIndex(body.loc.start) + 1, "let __temp, __restore;") then injects inside that expression instead of inside braces.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/acorn.ts` around lines 118 - 166, The async function body
handling in transformFunctionBody is assuming every async
ArrowFunctionExpression/FunctionExpression has a BlockStatement, which breaks
concise async arrow bodies like async () => await foo(). Update the logic to
detect expression-bodied async arrows before casting body, and either
wrap/convert them into a block or skip the block-only injection path so the
prepend/append edits only target real BlockStatement bodies. Use
transformFunctionBody, body, and the existing injectVariable/appendLeft flow to
ensure the __temp/__restore injection happens inside braces rather than inside
an expression.

Comment thread src/transform/oxc.ts
Comment on lines +65 to +73
if (node.type === "CallExpression") {
const functionName = _getFunctionName(node.callee);
if (options.asyncFunctions!.includes(functionName)) {
transformFunctionArguments(node);
if (functionName !== "callAsync") {
const lastArgument = node.arguments[node.arguments.length - 1];
if (lastArgument) {
s.appendRight(lastArgument.end, ",1");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Gate the extra ,1 argument on a rewrite in this callback.

Like the Acorn backend, this appends ,1 for every matched wrapper before knowing whether that callback actually contained a rewritten await. In files with multiple wrappers, untouched callbacks can be emitted with a changed call signature.

Also applies to: 169-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/oxc.ts` around lines 65 - 73, Gate the extra appended “,1” in
transformCallExpression/transformFunctionArguments logic so it is only added
when that callback actually had an await rewritten, instead of for every
matching asyncFunctions wrapper. Track whether the current CallExpression in
src/transform/oxc.ts was rewritten before calling s.appendRight on the last
argument, and apply the same fix to the corresponding callback handling around
the other referenced block so untouched wrappers keep their original call
signature.

Comment thread src/transform/oxc.ts
Comment on lines +118 to +166
function transformFunctionBody(function_: Node) {
if (
function_.type !== "ArrowFunctionExpression" &&
function_.type !== "FunctionExpression"
) {
return;
}

// No need to transform non-async function
if (!function_.async) {
return;
}

const body = function_.body as BlockStatement;

let injectVariable = false;
walk(body, {
enter(
node: MaybeHandledNode,
parent: MaybeHandledNode | undefined | null,
) {
if (node.type === "AwaitExpression" && !node[kInjected]) {
detected = true;
injectVariable = true;
injectForNode(node, parent);
} else if (
node.type === "IfStatement" &&
node.consequent.type === "ExpressionStatement" &&
node.consequent.expression.type === "AwaitExpression"
) {
detected = true;
injectVariable = true;
(node.consequent.expression as MaybeHandledNode)[kInjected] = true;
injectForNode(node.consequent.expression, node);
}
// Skip transform for nested functions
if (
node.type === "ArrowFunctionExpression" ||
node.type === "FunctionExpression" ||
node.type === "FunctionDeclaration"
) {
return this.skip();
}
},
});

if (injectVariable) {
s.appendLeft(body.start + 1, "let __temp, __restore;");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Expression-bodied async arrows break this rewrite path too.

async () => await foo() reaches this branch, but function_.body is an expression, not a BlockStatement. Injecting let __temp, __restore; at body.start + 1 therefore corrupts the expression instead of inserting into a block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/oxc.ts` around lines 118 - 166, The async function rewrite in
transformFunctionBody assumes function_.body is always a BlockStatement, but
expression-bodied async arrows like async () => await foo() have an expression
body and get corrupted by the injected variable declaration. Update
transformFunctionBody to detect non-block bodies before casting and rewriting,
and either wrap expression-bodied async arrows into a block or skip this rewrite
path; use the function_.body, injectVariable, and s.appendLeft(body.start + 1,
...) logic as the key places to adjust.

@TheAlexLichter

Copy link
Copy Markdown
Member Author

No huge perf difference at the moment as other operations take more effort. Have some ideas but this is on hold for now.

@TheAlexLichter TheAlexLichter marked this pull request as draft June 24, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants