loganfsmyth / babel-plugin-transform-decorators-legacy

A plugin for Babel 6 that (mostly) replicates the old decorator behavior from Babel 5
MIT License
817 stars 57 forks source link

Hook into initializer #74

Closed osi-oswald closed 6 years ago

osi-oswald commented 6 years ago

How can I hook into the initializer? Right now there is no way I could react to the initialized value, for example to keep two properties synced.

function syncToBar(target, key, descriptor) {
  let _value = descriptor.initializer();

  return {
    enumerable: true,
    configurable: true,
    get() { return _value; },
    set(value) {
      // not called for the initial value :-(
      this.bar = _value = value;
    }
  }
}

class MyClass {
  @syncToBar foo = 1;
  bar;

  constructor() {
    console.log(this.bar); // -> undefined, but I want it to be 1 :-(
    this.foo = 2;
    console.log(this.bar); // -> 2, OK (synced)
  }
}

Looking at the emitted code from transform-decorators-legacy, I can see no way to initialize bar with the initial value from foo with just the decorator. Am I missing something?

osi-oswald commented 6 years ago

Just figured, buildInitializerDefineProperty could be changed to this:

const buildInitializerDefineProperty = template(`
    function NAME(target, property, descriptor, context){
        if (!descriptor || !descriptor.initializer) return;
        context[property] = descriptor.initializer.call(context);
    }
`);

That way a setter defined on the prototype has a chance to react to the initialization assignment, whereas using Object.defineProperty will just skip the setter on the prototype :-/

osi-oswald commented 6 years ago

If I take a closer look at babel-plugin-transform-class-properties, I can see it has an option spec, which by default is false. If option spec is false (default), an assignment is emitted. And only of if spec is true, it will use Object.defineProperty to initialize.

Therefore: The default should be what I proposed in my last comment. And if one would be fancy, either read somehow the spec option from transform-class-properties or provide an own spec option to still emit Object.defineProperty for initialization.

loganfsmyth commented 6 years ago

spec exists because it is what the spec dictates, and it will default to true in future versions of Babel. The goal of this plugin is to mimic the behavior of decorators as they were implemented in Babel 5. I'm not looking to add enhancements like this.

What you want should be doable in future versions of the decorators spec, but implementing that is beyond the scope of this plugin. Until then, maybe you can get what you want by defining it as @syncFromFoo instead?