[labs/context] `@provide` decorator does not work with Babel
See discussion below.
Decorators work fine with TS.
With Babel:
@provide() on its own does not work.
@provide() @property() does not work.
@property() @provide actually seems to make the context providing work but value is not accessible from the host component.
Discussed in https://github.com/lit/lit/discussions/3804
Originally posted by jrj2211 April 13, 2023 I have an element that is providing some async loaded data from an API. I have children inside the element consuming the context, which is working.
I need my provider to also use the data being provided in the context, but it the property just comes back undefined?
import { userContext } from './contexts/user.js';
export class UserView extends LitElement {
@property({ attribute: false })
@provide({ context: userContext })
user;
render() {
return html`
<user-contact></user-contact>
<div>${this.user?.name}</div>
`;
}
async connectedCallback() {
super.connectedCallback();
const user = await App.client.service('users').findOne(this.id});
this.user = user;
console.log('provider', 'user', user) // not undefined
console.log('provider', 'user', this.user) // is undefined?
}
}
Logging out the loaded user, its undefined. The child user-contact that @consumes the userContext, gets the data.
Is this correct? The documentation show adding @property along with @provide, so why isn't the value available in the provider element via this.user?
Here are some findings after investigating.
The @lit-labs/context decorators make use if decorateProperty() https://github.com/lit/lit/blob/a1f8c345cc978be06c6416edd1228fc5471c53d8/packages/reactive-element/src/decorators/base.ts#L63
When this is used without providing a descriptor and only finisher as it is done with @provide(), in Babel mode this ends up creating the property on the instance with placement: "own" rather than the prototype, so the setter defined in the finisher ends up getting masked. It happened to be that if you do @property() @provide(), the @property() decorator was overriding this behavior and not creating an instance property, allowing the setter to work, but since the @provide() never sets a getter, its value is still not accessible.
When doing @provide() @property(), the finisher defined for @provide() never gets the correct property key as it gets the Symbol() from the @property() decorator. The code that's meant to handle this https://github.com/lit/lit/blob/a1f8c345cc978be06c6416edd1228fc5471c53d8/packages/reactive-element/src/decorators/base.ts#L89-L94 does not actually work as it seems originalKey is not carried over in Babel's implementation for chaining decorators.
I think the single @provide() use for babel is fixable by either making decorateProperty() always set the placement to prototype instead of own, and make sure the @provide() decorator accounts for data descriptors that have writable and value, or have the @provide() implementation just provide the getter/setter with the descriptor option for decorateProperty(). The latter has implications with chaining decorators or when the user provides getter/setter of their own.
Considering that Babel's removing support for the "2018-09" version (https://github.com/babel/babel/pull/12712), I wonder if it's worth our time to even consider fixing this.
cc: @justinfagnani
Not planning on supporting the nonstandard 2018 Babel transform with context