When, for example, a function is moved between from one place to another we recrawl and end up double counting any references it holds to the upper scope. This protects against that. (The same thing is done for constant violations in the `reassign` method)
The flag to control whether we should warn didn't take into account
nested calls or scope chains. An easier approach is to have a
counter. That way we know for sure if we're somewhere deep inside a
crawl call or not.
I previously tried an approach to scope bindings from var to scope but
it didn't catch all cases. This is evident in this bug:
https://phabricator.babeljs.io/T2892
Where even after transforming a const to a var we still get an error
that it's read-only.
This approach will go through and delete every existing let and const
binding and creates a new one with the kind "var"
As mentioned on the task https://phabricator.babeljs.io/T7179 having
this cache on the AST leads to all sorts of portability and reuse
bugs.
This moves the cache into a clearable WeakMap which will fix the
following:
1. Moving the AST between different babel versions or tools will not
lead into sharing potentially outdated cached information
2. `.clear()` can be called on the cache by a plugin to clear
potentially outdated information. This is helpful when implementing two
seperate pipelines that should not share information.
I think the next step (which is harder, I tried) is to isolate cache and
make it live on a transform or pipeline level state (like the `hub`).
The reason it is hard is because the `babel-traverse` main API -- although
requires the state object to be passed -- not many callers do. To fix
this we should release a patch version that warns about this and fix all
the internal callers. Next couple of releases we can start throwing when
no state is passed (or we can create our own state).
Original PR: https://github.com/babel/babel/pull/2469. Seems this got
lost in the v6 changes.
- - -
Without this, the only way to replace the arrow function is to either
manually override its `node.body`, or duplicate the arrow:
```js
// Old
ArrowFunctionExpression: function (node) {
node.body = t.blockStatement(...);
// Or
return t.ArrowFunctionExpression(
node.params,
t.blockStatement(...),
node.async
);
}
// New
ArrowFunctionExpression: function() {
this.get("body").replaceWith(t.blockStatement(...));
}
```
As an artificat of compiling methods to named function expressions the
function name is being considered a "local" binding in the function
body. This means that we will throw errors anytime someone would want to
create a new local binding with the same name.
This is solved by assigning a symbol to function Identifiers that
indicates that they should not be considered local bindings.