This is an overhaul of custom elements in Svelte. Instead of compiling to a custom element class, the Svelte component class is mostly preserved as-is. Instead a wrapper is introduced which wraps a Svelte component constructor and returns a HTML element constructor. This has a couple of advantages:
- component can be used both as a custom element as well as a regular component. This allows creating one wrapper custom element and using regular Svelte components inside. Fixes#3594, fixes#3128, fixes#4274, fixes#5486, fixes#3422, fixes#2969, helps with https://github.com/sveltejs/kit/issues/4502
- all components are compiled with injected styles (inlined through Javascript), fixes#4274
- the wrapper instantiates the component in `connectedCallback` and disconnects it in `disconnectedCallback` (but only after one tick, because this could be a element move). Mount/destroy works as expected inside, fixes#5989, fixes#8191
- the wrapper forwards `addEventListener` calls to `component.$on`, which allows to listen to custom events, fixes#3119, closes#4142
- some things are hard to auto-configure, like attribute hyphen preferences or whether or not setting a property should reflect back to the attribute. This is why `<svelte:options customElement={..}>` can also take an object to modify such aspects. This option allows to specify whether setting a prop should be reflected back to the attribute (default `false`), what to use when converting the property to the attribute value and vice versa (through `type`, default `String`, or when `export let prop = false` then `Boolean`), and what the corresponding attribute for the property is (`attribute`, default lowercased prop name). These options are heavily inspired by lit: https://lit.dev/docs/components/properties. Closes#7638, fixes#5705
- adds a `shadowdom` option to control whether or not encapsulate the custom element. Closes#4330, closes#1748
Breaking changes:
- Wrapped Svelte component now stays as a regular Svelte component (invokeing it like before with `new Component({ target: ..})` won't create a custom element). Its custom element constructor is now a static property named `element` on the class (`Component.element`) and should be regularly invoked through setting it in the html.
- The timing of mount/destroy/update is different. Mount/destroy/updating a prop all happen after a tick, so `shadowRoot.innerHTML` won't immediately reflect the change (Lit does this too). If you rely on it, you need to await a promise
* call onDestroy when disconnected
* lifecycle hooks and custom elements
- Call onMount in connectedCallback for customElements
- register onMount return values as on_disconnect-callbacks for customElements
- run on_disconnect callbacks in disconnectedCallback
* do not reset on_mount so that it can fire again if reinserted
* simpler isCustomElement & skip extra function call
- pass options.customElement down to mount_component
- remove expensive isCustomElement check
- only call add_render_callback if not customElement
Co-authored-by: Pontus Lundin <pontus.lundin@ica.se>