@litsx/light-dom-registry
v0.2.1
Published
Contextual light DOM registry runtime for LitSX element delegation
Readme
@litsx/light-dom-registry
Runtime support for contextual element resolution in LitSX static lightDom = true components.
Attribution
src/index.js includes code adapted from The Polymer Project's custom elements work. The original BSD-style attribution notice is preserved in the source file and in this package's NOTICE.
What It Does
@litsx/light-dom-registry lets static lightDom = true components keep clean base tags while still using different implementations for the same tag name in different light DOM subtrees.
It does that by:
- registering one global stand-in proxy per tag on
window.customElements - attaching a contextual registry to each
static lightDom = truehost - resolving the nearest host registry when a proxy instance is created or connected
- upgrading the real element in place, without adding a visible wrapper child
This is runtime infrastructure. The transform and LitSX runtime are still responsible for:
- emitting
static elements - choosing
LightDomElementsMixin - calling
connectLightDomRegistry(...)
Where The Global Proxy Is Registered
The global proxy registration happens when a light DOM registry defines a tag for the first time:
if (!standInClass) {
standInClass = createStandInElement(tagName);
standInClass[STAND_IN_MARK] = true;
nativeDefine(tagName, standInClass);
}That path lives in ShimmedCustomElementsRegistry#define(...).
There is also an explicit helper:
ensureLightDomProxy("profile-chip");which uses the same nativeDefine(...) path when the global stand-in does not exist yet.
Coexistence Demo
Two different light DOM hosts can use the same tag base and still resolve different constructors.
Authored Components
import { LitElement } from "lit";
export function AdminScreen() {
static lightDom = true;
return <profile-chip />;
}
AdminScreen.elements = {
"profile-chip": AdminProfileChip,
};
export function GuestScreen() {
static lightDom = true;
return <profile-chip />;
}
GuestScreen.elements = {
"profile-chip": GuestProfileChip,
};Lowered Shape
class AdminScreen extends LightDomElementsMixin(LitElement) {
static elements = {
"profile-chip": AdminProfileChip,
};
createRenderRoot() {
return this;
}
render() {
return <profile-chip />;
}
}
class GuestScreen extends LightDomElementsMixin(LitElement) {
static elements = {
"profile-chip": GuestProfileChip,
};
createRenderRoot() {
return this;
}
render() {
return <profile-chip />;
}
}Runtime Result
When both hosts are mounted in the same document:
<admin-screen>
<profile-chip></profile-chip>
</admin-screen>
<guest-screen>
<profile-chip></profile-chip>
</guest-screen>the two profile-chip nodes share the same globally registered proxy class, but each one upgrades against the nearest host registry:
- under
admin-screen,profile-chipupgrades toAdminProfileChip - under
guest-screen,profile-chipupgrades toGuestProfileChip
No suffix is added to the tag name, and no extra wrapper node is inserted.
Nested Host Demo
Nearest-host resolution also works when one light DOM host is nested inside another.
<outer-screen>
<profile-chip></profile-chip>
<inner-screen>
<profile-chip></profile-chip>
</inner-screen>
</outer-screen>If:
OuterScreen.elements["profile-chip"] = OuterProfileChipInnerScreen.elements["profile-chip"] = InnerProfileChip
then:
- the outer
profile-chipupgrades toOuterProfileChip - the inner
profile-chipupgrades toInnerProfileChip
The closest host context wins.
Collision Rule
If the base tag is already registered globally to a constructor that does not belong to this runtime, the registry throws instead of falling back to a suffixed tag.
That is deliberate. static lightDom = true is preserving the authored tag, not generating an alternative global name.
