Symptom
For a plain prop declared with reflect: true, calling el.setAttribute("v", "100") after the element has mounted does not update el.v. Pre-set attributes (set before append) parse correctly into the property; the regression only affects attribute changes after mount.
class C extends NudeElement {
static props = { v: { type: Number, reflect: true } };
}
customElements.define("x-c", C);
let el = new C();
document.body.append(el);
el.v = 42; // works → el.getAttribute("v") === "42"
el.setAttribute("v", "100");
// expected: el.v === 100
// actual: el.v === 42 (no attributeChangedCallback fired)
Root cause
Class.observedAttributes is wired via the static getter installed by first_constructor_static (plugins/props/index.js#L8–L23). That hook only runs the first time the class's constructor is called — i.e. on the first instance.
customElements.define(name, Class) reads Class.observedAttributes at registration time and caches the list. By that point, first_constructor_static has not yet run, so the static getter is undefined and the cached list is empty. Subsequent setAttribute() calls therefore never reach attributeChangedCallback.
The Props.initializeFor path manually walks observedAttributes on mount (after construction has run, so the getter is now in place), which is why pre-set attributes work.
Reproducible on
main and signals-props. Setup logic in first_constructor_static is byte-identical on both — this is pre-existing, not introduced by #91 / #97.
Suggested fix direction
Run the observedAttributes getter setup at class declaration time (e.g. via the setup static hook) instead of first_constructor_static. By the time customElements.define reads the list, the getter should already resolve to the right value.
Symptom
For a plain prop declared with
reflect: true, callingel.setAttribute("v", "100")after the element has mounted does not updateel.v. Pre-set attributes (set beforeappend) parse correctly into the property; the regression only affects attribute changes after mount.Root cause
Class.observedAttributesis wired via the static getter installed byfirst_constructor_static(plugins/props/index.js#L8–L23). That hook only runs the first time the class's constructor is called — i.e. on the first instance.customElements.define(name, Class)readsClass.observedAttributesat registration time and caches the list. By that point,first_constructor_statichas not yet run, so the static getter is undefined and the cached list is empty. SubsequentsetAttribute()calls therefore never reachattributeChangedCallback.The
Props.initializeForpath manually walksobservedAttributeson mount (after construction has run, so the getter is now in place), which is why pre-set attributes work.Reproducible on
mainandsignals-props. Setup logic infirst_constructor_staticis byte-identical on both — this is pre-existing, not introduced by #91 / #97.Suggested fix direction
Run the
observedAttributesgetter setup at class declaration time (e.g. via thesetupstatic hook) instead offirst_constructor_static. By the timecustomElements.definereads the list, the getter should already resolve to the right value.