Skip to content

Commit

Permalink
Correctly update bindings of decorated class declarations
Browse files Browse the repository at this point in the history
This commit adds a small utility (Scope#updateOwnBinding) which
should be used whenever you replace a declaration with a new
delaration which defines the same binding (e.g. `class Foo {}` ->
`let Foo = class Foo {}`). Since our scope system isn't currently
synchrinized with the AST, doing this automatically would add too
much overhead to the `replaceWith*` functions.

That function probably needs to be used in many more places, but we
can add them when users find bugs related to non-updated bingins.
  • Loading branch information
nicolo-ribaudo committed Aug 28, 2018
1 parent d45ea2e commit c4dec1c
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 0 deletions.
Expand Up @@ -252,6 +252,9 @@ export default {
const replacement = decoratedClassToExpression(path);
if (replacement) {
path.replaceWith(replacement);

const decl = path.get("declarations.0");
path.scope.updateOwnBinding(decl.node.id, path.node.kind, decl);
}
},
ClassExpression(path, state) {
Expand Down
@@ -0,0 +1,12 @@
import {autobind} from 'core-decorators';

export default function wrap() {
return function() {
class Foo {
@autobind
method() {}
}

return Foo;
};
}
@@ -0,0 +1,12 @@
{
"plugins": [
["proposal-decorators", { "legacy": true }]
],
"presets": [
["env", {
"targets": {
"node": 8
}
}]
]
}
@@ -0,0 +1,22 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = wrap;

var _coreDecorators = require("core-decorators");

function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; }

function wrap() {
return function () {
var _class;

let Foo = (_class = class Foo {
method() {}

}, (_applyDecoratedDescriptor(_class.prototype, "method", [_coreDecorators.autobind], Object.getOwnPropertyDescriptor(_class.prototype, "method"), _class.prototype)), _class);
return Foo;
};
}
11 changes: 11 additions & 0 deletions packages/babel-traverse/src/scope/index.js
Expand Up @@ -944,6 +944,17 @@ export default class Scope {
return this.parent && this.parent.hasBinding(name, noGlobals);
}

updateOwnBinding(id: t.Identifier, kind: string, path: NodePath) {
if (!kind) throw new ReferenceError("no `kind`");

const binding = this.getOwnBinding(id.name);
if (!binding) throw new ReferenceError(`Unknown binding: ${id.name}`);

binding.identifier = id;
binding.kind = kind;
binding.path = path;
}

/**
* Move a binding of `name` to another `scope`.
*/
Expand Down

0 comments on commit c4dec1c

Please sign in to comment.