Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

December meeting availability and agenda #104

Closed
Westbrook opened this issue Nov 21, 2024 · 7 comments
Closed

December meeting availability and agenda #104

Westbrook opened this issue Nov 21, 2024 · 7 comments

Comments

@Westbrook
Copy link
Collaborator

Please share you availability here to support scheduling out December meeting. The timing is a little different than we've had in the past as I'll be working "remotely". If that causes issues, I'm happy to pass the chairperson duties to someone else for the month if we'd really like to get a session in. 🆓

Don't forget, all WCCG events are listed on this calendar. 📆

As a reminder, we chat between meetings in Discord, where we'd love to hear from you. 🗣️

If there are other topics you'd like to discuss, breakouts you would already like to have scheduled, or anything else, please drop your thoughts in the comments below! 🙇🏼‍♂️

@Westbrook
Copy link
Collaborator Author

Agenda items that I know some people are interested in, if we chose to include them:

What would you want included?

@davatron5000
Copy link
Collaborator

Do we have an established community convention for this.???? = this.attachInternals(). Is that something we could talk about?

@Westbrook
Copy link
Collaborator Author

@davatron5000 happy to include this in the convo. Is there any additional background that the community should take into account in advance?

@sorvell
Copy link

sorvell commented Dec 5, 2024

Is there any additional background that the community should take into account in advance?

My own concern is with respect to subclassing. Consider that an element makes a private #internals and uses it to set some states. Then a subclasser cannot do anything else with ElementInternals.

My straw proposal is something like this:

static internals = Symbol();
constructor() {
  super();
  const {internals} = this.constructor as typeof MyElement;
  this[internals] ??= this.attachInternals();
}

@Westbrook
Copy link
Collaborator Author

That’s an interesting approach @sorvell. If this lines up with your thoughts (shape wise) @davatron5000, would it make sense to get an issue or PR into https://github.com/webcomponents-cg/community-protocols (as the user of Symbol() seems to imply protocol over spec)?

@Westbrook
Copy link
Collaborator Author

Westbrook commented Dec 17, 2024

Great discussion today! Thanks to all of your for making 2024 an amazing year for Web Components. 🙇🏼‍♂️ Let's do it again in 2025 👏🏼 👏🏼 👏🏼

Today's meeting notes.

Action Items

Meeting chat inside...

You
9:02 AM
https://docs.google.com/document/d/1jOz2Mu8y49j81WPE7__sHkyoUQey6X09JNiYmEh4b1I/edit?tab=t.0#heading=h.5njocyv0n2m
keep
Pinned
Dave Rupert
9:10 AM
Working on it. Not sure where referencetarget currently is at. I can try to follow up but will likely know more next year.
Owen Buckley
9:11 AM
whatwg/html#10854
Michael Warren
9:11 AM
i dig the new proposal, but it won’t help with MFEs much. but that’s ok. it is what it is haha
Owen Buckley
9:12 AM
challenge accepted! :D
Dave Rupert
9:12 AM
What's the ELI5 version of Webkit's suggested changes?
Michael Warren
9:13 AM
that’s totally fair. i was just hoping it might be doable. sounds like not, not a hill i’d die on.
+1
Dave Rupert
9:15 AM
ええ。日本へ行くのはラキーや。
You
9:15 AM
間違えないね!
Owen Buckley
9:15 AM
https://www.youtube.com/@keithamus/streams
Michael Warren
9:16 AM
that’s the best part imo
design systems can create registries, define els on them and pass em around which is great
Michael Warren
9:20 AM
LGTM, ship it haha. let’s start working on the react dom pr now so we can land it by 2032 :)
Michael Warren
9:21 AM
brb
back
Owen Buckley
9:24 AM
thanks for the rundown steve
Michael Warren
9:24 AM
apologies if i did de-rail. i tried not to cuz i do like the new version haha
Steve Orvell
9:25 AM
all good
Owen Buckley
9:25 AM
w3c/csswg-drafts#6867
Owen Buckley
9:27 AM
w3c/csswg-drafts#6867 (comment)
Dave Rupert
9:29 AM
:empty matches whitespace and that's a crime against everyone
and coments
Michael Warren
9:31 AM
+10000
Dave Rupert
9:31 AM
It's mentioned in there by westbrook, but better :host:has support would also re-inform the conversation I think.
Michael Warren
9:33 AM
that’s a decent time for me. i couldn’t do every week, but i could make it
Michael Warren
9:34 AM
brb
Owen Buckley
9:34 AM
webcomponents-cg/community-protocols#67
You
9:47 AM
https://lion.js.org/
You
9:51 AM
https://github.com/webcomponents-cg/community-protocols?tab=readme-ov-file#status
Michael Warren
9:52 AM
sorry it’s pool league night so i’m at a pool hall with my league team heh
Michael Warren
9:55 AM
no strong feelings so happy follow others lead on internals stuff
we’re implementing form controls now so it’s a good time to decide on something. we could just plug it into our specs
Dave Rupert
9:59 AM
I think it's a mostly theoretical FOUC because JS is required to register the element.
Michael Warren
10:00 AM
the issue i think about FOUC isn’t that shadow roots are competing with other js-created things. it’s because of the competition with ssr-created elements that aren’t created by js clientside
You
10:00 AM
https://github.com/webcomponents-cg/docs-and-guides
Michael Warren
10:00 AM
so you’re right, but the “battle” is really an ssr battle in those kinds of projects

@trusktr
Copy link

trusktr commented Dec 19, 2024

Do we have an established community convention for this.???? = this.attachInternals(). Is that something we could talk about?

I've touched on this before in various GitHub issues (probably search my username name and attachInternals together) but basically the issue is we don't have protected to easily share it from a base class to a subclass without making it public.

To solve this issue, the simplest proposal I could think of is making this.attachInternals() be essentially a getter that returns the instance if it was already created (instead of automatically throwing on any calls beyond the first), along with the requirement that it must throw an error if called outside of construction in order to prevent the public from accessing it on existing elements.

I'm assuming that this is possible with unique coordination with the JS engine in a way that JS cannot polyfill it currently (although I've asked for the ability to TC39 before, see below).

With this concept in place it would become super easy to share internals with subclasses:

class SomeFrameworkBaseClass extends HTMLElement {
  #internals 

  constructor() {
    super()
    this.#internals = this.attachInternals()
  }

  connectedCallback() {
    // Access internals without it being leaked to public
    this.#internals
  }
}

A subclass would then be able to easily get internals too:

class MyEl extends SomeFrameworkBaseClass {
  #internals

  constructor() {
    super()
    this.#internals = this.attachInternals()
  }

  connectedCallback() {
    super.connectedCallback()

    // Subclass can also use it!
    this.#internals
  }
}

Currently, the previous code sample will throw a runtime error. This idea would fix that.

People on the public usage side would not be able to get the internals:

const el = new MyEl()

el.attachInternals() // runtime error: cannot call attachInternals outside constructor.

el.#internals // syntax error

There's no way to exactly implement this concept in plain JS, it would require a mechanism that can only be implemented on the native side, but we can kind of emulate it to a certain extent:

class HTMLElement {
  #isConstructing = true
  constructor() {
    queueMicrotask(() => this.#isConstructing = false)
  }

  attachInternals() {
    if (!this.#isConstructing) throw new Error('Can only call attachInternals in constructor')

    return theInternals
  }
}

// User code:
const el = new SomeEl

el.attachInternals() // this currently does not error with the example, but the native version would

setTimeout(() => {
  el.attachInternals() // throws
})

Here are some ways to do this natively:

  1. some special trick with the JS engine so that the private field is set to false immediately after the constructor call stack.

    • I've asked for a way to do this here, although maybe decorators are not specifically the way the feature could be exposed:
    • there may be other ways than with decorators for the JS engine to expose this functionality, for example maybe Reflect.afterConstruct(() => {}) could be called inside a constructor to give the engine a callback to call after the constructor stack, or something.
  2. always have attachInternals throw if not called inside of createElement, which does not require special JS engine treatment, the DOM implementation can set the state back to false before returning the instance. new SomeEl would cause it to throw, but document.createElement('some-el') would work.

    • this would be polyfillable in JS.
  3. always have attachInternals throw if construction is not wrapped in a Proxy that traps construct to set isConstructing back to false

    • this would be polyfillable in JS.
    • it would work like this:
    class _MyEl extends HTMLElement {
      constructor() {
        this.attachInternals()
      }
    }
    
    // define() is updated to return a Proxy
    const MyEl = customElements.define('my-el', _MyEl)
    
    new _MyEl() // throws
    new MyEl() // does not throw
  4. Another idea is that once decorators are out, web APIs could give people a new decorator to make attachInternals throw in constructor by wrapping the class (in order to be able to set private state back to false, f.e. isConstructing). This idea is interesting because it would be straight forward:

    const {privateInternals} = HTMLElement
    
    @privateInternals
    class MyEl extends HTMLElement {
      constructor () {
        this.attachInternals() // throws anywhere else but in constructor.
      }
    }

    I believe this decorator could use new.target to figure after which subclass to set private state back to false, in case multiple subclasses in the hierarchy are decorated with it.

    • I think this can be polyfilled today with Babel/TS decorators (and will continue to work when decorators land). Maybe this one is worthy of a community protocol?

My straw proposal is something like this:

static internals = Symbol();
constructor() {
  super();
  const {internals} = this.constructor as typeof MyElement;
  this[internals] ??= this.attachInternals();
}

With this pattern the internals would still be easy to access from the public side:

import {Base} from 'some-lib'

const el = document.querySelector('some-el') // some el that extends from Base

const internals = el[Base.internals]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants