diff --git a/CHANGELOG.md b/CHANGELOG.md index 888f0bc626c..c6c8f6a94b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ If you pass a non-JavaScript file such as a `.json` file to esbuild, it will by default generate `module.exports = {...}`. However, the `module` variable would incorrectly be minified when `--minify` is present. This issue has been fixed. This bug did not appear if `--format=cjs` was also present, only if no `--format` flag was specified. +* Fix bugs with `async` functions ([#388](https://github.com/evanw/esbuild/issues/388)) + + This release contains correctness fixes for `async` arrow functions with regard to the `arguments` variable. This affected `async` arrow functions nested inside `function` expressions or statements. Part of this fix was contributed by [@rtsao](https://github.com/rtsao). + ## 0.7.1 * Fix bug that forbids `undefined` values in the JavaScript API diff --git a/internal/bundler/snapshots/snapshots_lower.txt b/internal/bundler/snapshots/snapshots_lower.txt index b62aa07d48f..6e48454c33e 100644 --- a/internal/bundler/snapshots/snapshots_lower.txt +++ b/internal/bundler/snapshots/snapshots_lower.txt @@ -51,9 +51,10 @@ export default [ } }, function() { - return (_0, ..._1) => __async(this, [_0, ..._1], function* (bar) { + var _arguments = arguments; + return (bar) => __async(this, null, function* () { yield bar; - return [this, arguments]; + return [this, _arguments]; }); } ]; diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 3ad58d57f10..3a30d3feadb 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -176,10 +176,15 @@ type parser struct { typeofRequireEqualsFnTarget js_ast.E // Temporary variables used for lowering - tempRefsToDeclare []js_ast.Ref + tempRefsToDeclare []tempRef tempRefCount int } +type tempRef struct { + ref js_ast.Ref + value *js_ast.Expr +} + const ( locModuleScope = -1 ) @@ -239,8 +244,35 @@ type fnOrArrowDataVisit struct { // restored on the call stack around code that parses nested functions (but not // nested arrow functions). type fnOnlyDataVisit struct { - isThisCaptured bool - argumentsRef *js_ast.Ref + // This is a reference to the magic "arguments" variable that exists inside + // functions in JavaScript. It will be non-nil inside functions and nil + // otherwise. + argumentsRef *js_ast.Ref + + // Arrow functions don't capture the value of "this" and "arguments". Instead, + // the values are inherited from the surrounding context. If arrow functions + // are turned into regular functions due to lowering, we will need to generate + // local variables to capture these values so they are preserved correctly. + thisCaptureRef *js_ast.Ref + argumentsCaptureRef *js_ast.Ref + + // If we're inside an async arrow function and async functions are not + // supported, then we will have to convert that arrow function to a generator + // function. That means references to "arguments" inside the arrow function + // will have to reference a captured variable instead of the real variable. + isInsideAsyncArrowFn bool + + // If false, the value for "this" is the top-level module scope "this" value. + // That means it's "undefined" for ECMAScript modules and "exports" for + // CommonJS modules. We track this information so that we can substitute the + // correct value for these top-level "this" references at compile time instead + // of passing the "this" expression through to the output and leaving the + // interpretation up to the run-time behavior of the generated code. + // + // If true, the value for "this" is nested inside something (either a function + // or a class declaration). That means the top-level module scope "this" value + // has been shadowed and is now inaccessible. + isThisNested bool } const bloomFilterSize = 251 @@ -5559,7 +5591,7 @@ func (p *parser) generateTempRef(declare generateTempRefArg, optionalName string } ref := p.newSymbol(js_ast.SymbolOther, optionalName) if declare == tempRefNeedsDeclare { - p.tempRefsToDeclare = append(p.tempRefsToDeclare, ref) + p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ref: ref}) } scope.Generated = append(scope.Generated, ref) return ref @@ -5709,20 +5741,45 @@ func shouldKeepStmtInDeadControlFlow(stmt js_ast.Stmt) bool { } } -func (p *parser) visitStmtsAndPrependTempRefs(stmts []js_ast.Stmt) []js_ast.Stmt { +type prependTempRefsOpts struct { + fnBodyLoc *logger.Loc +} + +func (p *parser) visitStmtsAndPrependTempRefs(stmts []js_ast.Stmt, opts prependTempRefsOpts) []js_ast.Stmt { oldTempRefs := p.tempRefsToDeclare oldTempRefCount := p.tempRefCount - p.tempRefsToDeclare = []js_ast.Ref{} + p.tempRefsToDeclare = nil p.tempRefCount = 0 stmts = p.visitStmts(stmts) + // Prepend values for "this" and "arguments" + if opts.fnBodyLoc != nil { + // Capture "this" + if ref := p.fnOnlyDataVisit.thisCaptureRef; ref != nil { + p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ + ref: *ref, + value: &js_ast.Expr{Loc: *opts.fnBodyLoc, Data: &js_ast.EThis{}}, + }) + p.currentScope.Generated = append(p.currentScope.Generated, *ref) + } + + // Capture "arguments" + if ref := p.fnOnlyDataVisit.argumentsCaptureRef; ref != nil { + p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ + ref: *ref, + value: &js_ast.Expr{Loc: *opts.fnBodyLoc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.argumentsRef}}, + }) + p.currentScope.Generated = append(p.currentScope.Generated, *ref) + } + } + // Prepend the generated temporary variables to the beginning of the statement list if len(p.tempRefsToDeclare) > 0 { decls := []js_ast.Decl{} - for _, ref := range p.tempRefsToDeclare { - decls = append(decls, js_ast.Decl{Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: ref}}}) - p.recordDeclaredSymbol(ref) + for _, temp := range p.tempRefsToDeclare { + decls = append(decls, js_ast.Decl{Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: temp.ref}}, Value: temp.value}) + p.recordDeclaredSymbol(temp.ref) } // If the first statement is a super() call, make sure it stays that way @@ -7151,7 +7208,7 @@ func (p *parser) visitAndAppendStmt(stmts []js_ast.Stmt, stmt js_ast.Stmt) []js_ p.enclosingNamespaceRef = &s.Arg p.pushScopeForVisitPass(js_ast.ScopeEntry, stmt.Loc) p.recordDeclaredSymbol(s.Arg) - stmtsInsideNamespace := p.visitStmtsAndPrependTempRefs(s.Stmts) + stmtsInsideNamespace := p.visitStmtsAndPrependTempRefs(s.Stmts, prependTempRefsOpts{}) p.popScope() p.enclosingNamespaceRef = oldEnclosingNamespaceRef @@ -7347,8 +7404,8 @@ func (p *parser) visitClass(class *js_ast.Class) { *class.Extends = p.visitExpr(*class.Extends) } - oldIsThisCaptured := p.fnOnlyDataVisit.isThisCaptured - p.fnOnlyDataVisit.isThisCaptured = true + oldIsThisCaptured := p.fnOnlyDataVisit.isThisNested + p.fnOnlyDataVisit.isThisNested = true // A scope is needed for private identifiers p.pushScopeForVisitPass(js_ast.ScopeClassBody, class.BodyLoc) @@ -7371,7 +7428,7 @@ func (p *parser) visitClass(class *js_ast.Class) { } } - p.fnOnlyDataVisit.isThisCaptured = oldIsThisCaptured + p.fnOnlyDataVisit.isThisNested = oldIsThisCaptured } func (p *parser) visitArgs(args []js_ast.Arg) { @@ -7763,7 +7820,7 @@ func (p *parser) visitExpr(expr js_ast.Expr) js_ast.Expr { } func (p *parser) valueForThis(loc logger.Loc) (js_ast.Expr, bool) { - if p.Mode != config.ModePassThrough && !p.fnOnlyDataVisit.isThisCaptured { + if p.Mode != config.ModePassThrough && !p.fnOnlyDataVisit.isThisNested { if p.hasES6ImportSyntax || p.hasES6ExportSyntax { // In an ES6 module, "this" is supposed to be undefined. Instead of // doing this at runtime using "fn.call(undefined)", we do it at @@ -8982,12 +9039,21 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO isAsync: e.IsAsync, } + // Mark if we're inside an async arrow function. This value should be true + // even if we're inside multiple arrow functions and the closest inclosing + // arrow function isn't async, as long as at least one enclosing arrow + // function within the current enclosing function is async. + oldInsideAsyncArrowFn := p.fnOnlyDataVisit.isInsideAsyncArrowFn + if e.IsAsync { + p.fnOnlyDataVisit.isInsideAsyncArrowFn = true + } + p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, expr.Loc) p.visitArgs(e.Args) p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, e.Body.Loc) - e.Body.Stmts = p.visitStmtsAndPrependTempRefs(e.Body.Stmts) + e.Body.Stmts = p.visitStmtsAndPrependTempRefs(e.Body.Stmts, prependTempRefsOpts{}) p.popScope() - p.lowerFunction(&e.IsAsync, &e.Args, e.Body.Loc, &e.Body.Stmts, &e.PreferExpr, &e.HasRestArg, true) + p.lowerFunction(&e.IsAsync, &e.Args, e.Body.Loc, &e.Body.Stmts, &e.PreferExpr, &e.HasRestArg, true /* isArrow */) p.popScope() if p.MangleSyntax && len(e.Body.Stmts) == 1 { @@ -9002,6 +9068,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO } } + p.fnOnlyDataVisit.isInsideAsyncArrowFn = oldInsideAsyncArrowFn p.fnOrArrowDataVisit = oldFnOrArrowData case *js_ast.EFunction: @@ -9047,6 +9114,13 @@ func (p *parser) valueForDefine(loc logger.Loc, assignTarget js_ast.AssignTarget func (p *parser) handleIdentifier(loc logger.Loc, assignTarget js_ast.AssignTarget, e *js_ast.EIdentifier) js_ast.Expr { ref := e.Ref + // Capture the "arguments" variable if necessary + if p.fnOnlyDataVisit.isInsideAsyncArrowFn && p.UnsupportedFeatures.Has(compat.AsyncAwait) && + p.fnOnlyDataVisit.argumentsRef != nil && ref == *p.fnOnlyDataVisit.argumentsRef { + ref = p.captureArguments() + e.Ref = ref + } + if p.Mode == config.ModeBundle && assignTarget != js_ast.AssignTargetNone { if p.symbols[ref.InnerIndex].Kind == js_ast.SymbolImport { // Create an error for assigning to an import namespace @@ -9120,8 +9194,8 @@ func (p *parser) visitFn(fn *js_ast.Fn, scopeLoc logger.Loc) { isAsync: fn.IsAsync, } p.fnOnlyDataVisit = fnOnlyDataVisit{ - isThisCaptured: true, - argumentsRef: &fn.ArgumentsRef, + isThisNested: true, + argumentsRef: &fn.ArgumentsRef, } if fn.Name != nil { @@ -9131,9 +9205,9 @@ func (p *parser) visitFn(fn *js_ast.Fn, scopeLoc logger.Loc) { p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, scopeLoc) p.visitArgs(fn.Args) p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, fn.Body.Loc) - fn.Body.Stmts = p.visitStmtsAndPrependTempRefs(fn.Body.Stmts) + fn.Body.Stmts = p.visitStmtsAndPrependTempRefs(fn.Body.Stmts, prependTempRefsOpts{fnBodyLoc: &fn.Body.Loc}) p.popScope() - p.lowerFunction(&fn.IsAsync, &fn.Args, fn.Body.Loc, &fn.Body.Stmts, nil, &fn.HasRestArg, false) + p.lowerFunction(&fn.IsAsync, &fn.Args, fn.Body.Loc, &fn.Body.Stmts, nil, &fn.HasRestArg, false /* isArrow */) p.popScope() p.fnOrArrowDataVisit = oldFnOrArrowData @@ -9376,7 +9450,7 @@ func (p *parser) appendPart(parts []js_ast.Part, stmts []js_ast.Stmt) []js_ast.P p.importRecordsForCurrentPart = nil p.scopesForCurrentPart = nil part := js_ast.Part{ - Stmts: p.visitStmtsAndPrependTempRefs(stmts), + Stmts: p.visitStmtsAndPrependTempRefs(stmts, prependTempRefsOpts{}), SymbolUses: p.symbolUses, } if len(part.Stmts) > 0 { diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index eddf472e93d..6d2830d6d05 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -132,6 +132,22 @@ func (p *parser) isPrivateUnsupported(private *js_ast.EPrivateIdentifier) bool { return p.UnsupportedFeatures.Has(p.symbols[private.Ref.InnerIndex].Kind.Feature()) } +func (p *parser) captureThis() js_ast.Ref { + if p.fnOnlyDataVisit.thisCaptureRef == nil { + ref := p.newSymbol(js_ast.SymbolHoisted, "_this") + p.fnOnlyDataVisit.thisCaptureRef = &ref + } + return *p.fnOnlyDataVisit.thisCaptureRef +} + +func (p *parser) captureArguments() js_ast.Ref { + if p.fnOnlyDataVisit.argumentsCaptureRef == nil { + ref := p.newSymbol(js_ast.SymbolHoisted, "_arguments") + p.fnOnlyDataVisit.argumentsCaptureRef = &ref + } + return *p.fnOnlyDataVisit.argumentsCaptureRef +} + func (p *parser) lowerFunction( isAsync *bool, args *[]js_ast.Arg, @@ -218,23 +234,25 @@ func (p *parser) lowerFunction( } *bodyStmts = nil + // Errors thrown during argument evaluation must reject the + // resulting promise, which needs more complex code to handle + couldThrowErrors := false + for _, arg := range *args { + if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok || (arg.Default != nil && couldPotentiallyThrow(arg.Default.Data)) { + couldThrowErrors = true + break + } + } + // Forward the arguments to the wrapper function - usesArguments := p.fnOnlyDataVisit.argumentsRef != nil && p.symbolUses[*p.fnOnlyDataVisit.argumentsRef].CountEstimate > 0 + usesArgumentsRef := !isArrow && p.fnOnlyDataVisit.argumentsRef != nil && p.symbolUses[*p.fnOnlyDataVisit.argumentsRef].CountEstimate > 0 var forwardedArgs js_ast.Expr - if len(*args) == 0 && !usesArguments { - // Don't allocate anything if arguments aren't needed + if !couldThrowErrors && !usesArgumentsRef { + // Simple case: the arguments can stay on the outer function. It's + // worth separating out the simple case because it's the common case + // and it generates smaller code. forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.ENull{}} } else { - // Errors thrown during argument evaluation must reject the - // resulting promise, which needs more complex code to handle - couldThrowErrors := false - for _, arg := range *args { - if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok || (arg.Default != nil && couldPotentiallyThrow(arg.Default.Data)) { - couldThrowErrors = true - break - } - } - // If code uses "arguments" then we must move the arguments to the inner // function. This is because you can modify arguments by assigning to // elements in the "arguments" object: @@ -244,60 +262,77 @@ func (p *parser) lowerFunction( // // "x" must be 1 here // } // - if !couldThrowErrors && !usesArguments { - // Simple case: the arguments can stay on the outer function. It's - // worth separating out the simple case because it's the common case - // and it generates smaller code. - forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.ENull{}} - } else { - // Complex case: the arguments must be moved to the inner function - fn.Args = *args - fn.HasRestArg = *hasRestArg - *args = nil - *hasRestArg = false - - // Make sure to not change the value of the "length" property - for i, arg := range fn.Args { - if arg.Default != nil || fn.HasRestArg && i+1 == len(fn.Args) { - // Arguments from here on don't add to the "length" - break - } - // Generate a dummy variable - argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", i)) - p.currentScope.Generated = append(p.currentScope.Generated, argRef) - *args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: arg.Binding.Loc, Data: &js_ast.BIdentifier{Ref: argRef}}}) + // Complex case: the arguments must be moved to the inner function + fn.Args = *args + fn.HasRestArg = *hasRestArg + *args = nil + *hasRestArg = false + + // Make sure to not change the value of the "length" property. This is + // done by generating dummy arguments for the outer function equal to + // the expected length of the function: + // + // async function foo(a, b, c = d, ...e) { + // } + // + // This turns into: + // + // function foo(_0, _1) { + // return __async(this, arguments, function* (a, b, c = d, ...e) { + // }); + // } + // + // The "_0" and "_1" are dummy variables to ensure "foo.length" is 2. + for i, arg := range fn.Args { + if arg.Default != nil || fn.HasRestArg && i+1 == len(fn.Args) { + // Arguments from here on don't add to the "length" + break } - // Forward all arguments from the outer function to the inner function - if !isArrow { - // Normal functions can just use "arguments" to forward everything - forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.argumentsRef}} - } else { - // Arrow functions can't use "arguments", so we need to forward - // the arguments manually - - // If we need to forward more than the current number of arguments, - // add a rest argument to the set of forwarding variables - if usesArguments || fn.HasRestArg || len(*args) < len(fn.Args) { - argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", len(*args))) - p.currentScope.Generated = append(p.currentScope.Generated, argRef) - *args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: bodyLoc, Data: &js_ast.BIdentifier{Ref: argRef}}}) - *hasRestArg = true - } + // Generate a dummy variable + argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", i)) + p.currentScope.Generated = append(p.currentScope.Generated, argRef) + *args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: arg.Binding.Loc, Data: &js_ast.BIdentifier{Ref: argRef}}}) + } - // Forward all of the arguments - items := make([]js_ast.Expr, 0, len(*args)) - for i, arg := range *args { - id := arg.Binding.Data.(*js_ast.BIdentifier) - item := js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}} - if *hasRestArg && i+1 == len(*args) { - item.Data = &js_ast.ESpread{Value: item} - } - items = append(items, item) + // Forward all arguments from the outer function to the inner function + if !isArrow { + // Normal functions can just use "arguments" to forward everything + forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.argumentsRef}} + } else { + // Arrow functions can't use "arguments", so we need to forward + // the arguments manually. + // + // Note that if the arrow function references "arguments" in its body + // (even if it's inside another nested arrow function), that reference + // to "arguments" will have to be subsituted with a captured variable. + // This is because we're changing the arrow function into a generator + // function, which introduces a variable named "arguments". This is + // handled separately during symbol resolution instead of being handled + // here so we don't need to re-traverse the arrow function body. + + // If we need to forward more than the current number of arguments, + // add a rest argument to the set of forwarding variables. This is the + // case if the arrow function has rest or default arguments. + if len(*args) < len(fn.Args) { + argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", len(*args))) + p.currentScope.Generated = append(p.currentScope.Generated, argRef) + *args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: bodyLoc, Data: &js_ast.BIdentifier{Ref: argRef}}}) + *hasRestArg = true + } + + // Forward all of the arguments + items := make([]js_ast.Expr, 0, len(*args)) + for i, arg := range *args { + id := arg.Binding.Data.(*js_ast.BIdentifier) + item := js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}} + if *hasRestArg && i+1 == len(*args) { + item.Data = &js_ast.ESpread{Value: item} } - forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EArray{Items: items, IsSingleLine: true}} + items = append(items, item) } + forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EArray{Items: items, IsSingleLine: true}} } } @@ -2087,7 +2122,8 @@ func (p *parser) maybeLowerSuperPropertyAccessInsideCall(call *js_ast.ECall) { func couldPotentiallyThrow(data js_ast.E) bool { switch data.(type) { - case *js_ast.ENull, *js_ast.EUndefined, *js_ast.EBoolean, *js_ast.ENumber, *js_ast.EBigInt, *js_ast.EString, *js_ast.EFunction, *js_ast.EArrow: + case *js_ast.ENull, *js_ast.EUndefined, *js_ast.EBoolean, *js_ast.ENumber, + *js_ast.EBigInt, *js_ast.EString, *js_ast.EFunction, *js_ast.EArrow: return false } return true diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index ed1388a1e6c..7558d741771 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -1032,20 +1032,6 @@ in.js:24:30: warning: Writing to getter-only property "#getter" will throw ) `, }, { async: true }), - test(['in.js', '--outfile=node.js', '--target=es6'], - { - 'in.js': ` - function nonArrowWrapper() { - return async (x, paramWithDefault = {}) => { - if (x !== 123) { - throw 'fail'; - } - console.log(paramWithDefault); - }; - } - exports.async = () => nonArrowWrapper()(123); - `, - }, { async: true }), test(['in.js', '--outfile=node.js', '--target=es6'], { 'in.js': ` exports.async = async () => { @@ -1058,6 +1044,46 @@ in.js:24:30: warning: Writing to getter-only property "#getter" will throw } `, }, { async: true }), + test(['in.js', '--outfile=node.js', '--target=es6'], { + 'in.js': ` + let couldThrow = () => 'b' + exports.async = async () => { + "use strict" + async function f0() { + let bar = async (x, y) => [x, y, this, arguments] + return await bar('a', 'b') + } + async function f1() { + let bar = async (x, ...y) => [x, y[0], this, arguments] + return await bar('a', 'b') + } + async function f2() { + let bar = async (x, y = 'b') => [x, y, this, arguments] + return await bar('a') + } + async function f3() { + let bar = async (x, y = couldThrow()) => [x, y, this, arguments] + return await bar('a') + } + async function f4() { + let bar = async (x, y = couldThrow()) => (() => [x, y, this, arguments])() + return await bar('a') + } + async function f5() { + let bar = () => async (x, y = couldThrow()) => [x, y, this, arguments] + return await bar()('a') + } + async function f6() { + let bar = async () => async (x, y = couldThrow()) => [x, y, this, arguments] + return await (await bar())('a') + } + for (let foo of [f0, f1, f2, f3, f4, f5, f6]) { + let [x, y, t, a] = await foo.call(0, 1, 2, 3) + if (x !== 'a' || y !== 'b' || t !== 0 || a.length !== 3 || a[0] !== 1 || a[1] !== 2 || a[2] !== 3) throw 'fail' + } + } + `, + }, { async: true }), test(['in.js', '--outfile=node.js', '--target=es6'], { // The async transform must not change the argument count 'in.js': `