Jarred Sumner
|
37c41137f8
|
feat(transpiler): add replMode option for REPL transforms (#26246)
## Summary
Add a new `replMode` option to `Bun.Transpiler` that transforms code for
interactive REPL evaluation. This enables building a Node.js-compatible
REPL using `Bun.Transpiler` with `vm.runInContext` for persistent
variable scope.
## Features
- **Expression result capture**: Wraps the last expression in `{
__proto__: null, value: expr }` for result capture
- **IIFE wrappers**: Uses sync/async IIFE wrappers to avoid extra
parentheses around object literals in output
- **Variable hoisting**: Hoists `var`/`let`/`const` declarations outside
the IIFE for persistence across REPL lines
- **const → let conversion**: Converts `const` to `let` for REPL
mutability (allows re-declaration)
- **Function hoisting**: Hoists function declarations with
`this.funcName = funcName` assignment for vm context persistence
- **Class hoisting**: Hoists class declarations with `var` for vm
context persistence
- **Object literal detection**: Auto-detects object literals (code
starting with `{` without trailing `;`) and wraps them in parentheses
## Usage
```typescript
import vm from "node:vm";
const transpiler = new Bun.Transpiler({
loader: "tsx",
replMode: true,
});
const context = vm.createContext({ console, Promise });
async function repl(code: string) {
const transformed = transpiler.transformSync(code);
const result = await vm.runInContext(transformed, context);
return result.value;
}
// Example REPL session
await repl("var x = 10"); // 10
await repl("x + 5"); // 15
await repl("class Counter {}"); // [class Counter]
await repl("new Counter()"); // Counter {}
await repl("{a: 1, b: 2}"); // {a: 1, b: 2} (auto-detected object literal)
await repl("await Promise.resolve(42)"); // 42
```
## Transform Examples
| Input | Output |
|-------|--------|
| `42` | `(() => { return { __proto__: null, value: 42 }; })()` |
| `var x = 10` | `var x; (() => { return { __proto__: null, value: x =
10 }; })()` |
| `await fetch()` | `(async () => { return { __proto__: null, value:
await fetch() }; })()` |
| `{a: 1}` | `(() => { return { __proto__: null, value: ({a: 1}) };
})()` |
| `class Foo {}` | `var Foo; (() => { return { __proto__: null, value:
Foo = class Foo {} }; })()` |
## Files Changed
- `src/ast/repl_transforms.zig`: New module containing REPL transform
logic
- `src/ast/P.zig`: Calls REPL transforms after parsing in REPL mode
- `src/bun.js/api/JSTranspiler.zig`: Adds `replMode` config option and
object literal detection
- `src/options.zig`, `src/runtime.zig`, `src/transpiler.zig`: Propagate
`repl_mode` flag
- `packages/bun-types/bun.d.ts`: TypeScript type definitions
- `test/js/bun/transpiler/repl-transform.test.ts`: Test cases
## Testing
```bash
bun bd test test/js/bun/transpiler/repl-transform.test.ts
```
34 tests covering:
- Basic transform output
- REPL session with node:vm
- Variable persistence across lines
- Object literal detection
- Edge cases (empty input, comments, TypeScript, etc.)
- No-transform cases (await inside async functions)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
|
2026-01-21 13:39:25 -08:00 |
|