babel / babel/babel

[Bug]: plugin-transform-modules-commonjs does not transform `arguments` outside function

Open
#13,547 1 comment 0 reactions 0 assignees View on GitHub
i: needs triage
Dominant language
TypeScript
Stars
44k
Forks
6k
Avg merge
5d 15h
Merged PRs (30d)
23

Description

### 💻

- [ ] Would you like to work on a fix?

### How are you using Babel?

@babel/register or @babel/node

### Input code

```js
// ES Module
const a = arguments;
```

[REPL](https://babeljs.io/repl/#?browsers=&build=&builtIns=false&corejs=3.6&spec=false&loose=false&code_lz=MYewdgzgLgBAhjAvPATgcwK4FsCmYoQDcAUEA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=&prettier=true&targets=Node-16&version=7.14.7&externalPlugins=%40babel%2Fplugin-transform-modules-commonjs%407.14.5)

### Configuration file name

babel.config.json

### Configuration

```json
{
"sourceType": "module",
"plugins": [ "@babel/plugin-transform-modules-commonjs" ]
}
```

### Current and expected behavior

In an ES Module, `arguments` outside of a function evaluates to `globalThis.arguments` if it's defined, or throws a runtime error `ReferenceError: arguments is not defined`.

In CommonJS, `arguments` evaluates to the arguments object of the CommonJS wrapper function (`module`, `exports`, `require` etc).

[@babel/plugin-transform-modules-commonjs](https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs) does not take this difference into account. `arguments` is passed through unchanged.

### Environment

```
System:
OS: macOS 10.15.7
Binaries:
Node: 16.4.2 - ~/.nvm/versions/node/v16.4.2/bin/node
npm: 7.18.1 - ~/.nvm/versions/node/v16.4.2/bin/npm
npmPackages:
@babel/core: ^7.14.6 => 7.14.6
@babel/generator: ^7.14.5 => 7.14.5
@babel/helper-module-transforms: ^7.14.5 => 7.14.5
@babel/parser: ^7.14.7 => 7.14.7
@babel/plugin-transform-arrow-functions: ^7.14.5 => 7.14.5
@babel/plugin-transform-modules-commonjs: ^7.14.5 => 7.14.5
@babel/plugin-transform-react-jsx: ^7.14.5 => 7.14.5
@babel/plugin-transform-strict-mode: ^7.14.5 => 7.14.5
@babel/register: ^7.14.5 => 7.14.5
@babel/traverse: ^7.14.7 => 7.14.7
@babel/types: ^7.14.5 => 7.14.5
babel-jest: ^27.0.6 => 27.0.6
babel-plugin-dynamic-import-node: ^2.3.3 => 2.3.3
eslint: ^7.30.0 => 7.30.0
jest: ^27.0.6 => 27.0.6
```

### Possible solution

A more correct transformation would be:

```js
// ESM input
const a = arguments;
```

```js
// CommonJS output
const a = (0, eval)("arguments");
```

Only problem with above implementation is that `eval()` can be blocked by CSP in some environments. An alternative:

```js
// CommonJS output
const a = _helper_getArguments();

function _helper_getArguments() {
if ( 'arguments' in globalThis ) return globalThis.arguments;
throw new ReferenceError( "arguments is not defined" );
}
```

But then you need a cross-platform implementation of `globalThis` which is [surprisingly tricky](https://mathiasbynens.be/notes/globalthis).

Maybe, given how rare it would be to define `globalThis.arguments`, better to always throw.

### Additional context

_No response_

Contributor guide

Open the contributing guide

Research direction

Start at the @babel/plugin-transform-modules-commonjs entry point and reproduce the issue with the provided ES module input, babel.config.json, and Babel REPL configuration. Compare the generated CommonJS behavior with ES module semantics for top-level `arguments`, then determine and test a transformation that handles both the global value and the required ReferenceError behavior without changing function-scoped `arguments`.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.