Skip to content

Commit 3813889

Browse files
author
Steve Orvell
committed
handle connection and add more tests
1 parent 5658304 commit 3813889

File tree

2 files changed

+330
-50
lines changed

2 files changed

+330
-50
lines changed

packages/scoped-custom-element-registry/src/scoped-custom-element-registry.ts

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -397,23 +397,20 @@ window.HTMLElement = (function HTMLElement(this: HTMLElement) {
397397
window.HTMLElement.prototype = NativeHTMLElement.prototype;
398398

399399
// Helpers to return the scope for a node where its registry would be located
400-
// const isValidScope = (node: Node) =>
401-
// node === document || node instanceof ShadowRoot;
402-
const registryForNode = (node: Node): ShimmedCustomElementsRegistry | null => {
400+
const registryFromContext = (
401+
node: Element
402+
): ShimmedCustomElementsRegistry | null => {
403+
const explicitRegistry = registryForElement.get(node);
404+
if (explicitRegistry != null) {
405+
return explicitRegistry;
406+
}
403407
const context = creationContext[creationContext.length - 1];
404408
if (context instanceof CustomElementRegistry) {
405409
return context as ShimmedCustomElementsRegistry;
406410
}
407-
if (
408-
context?.nodeType === Node.ELEMENT_NODE ||
409-
context?.nodeType === Node.DOCUMENT_FRAGMENT_NODE
410-
) {
411-
return context.customElements as ShimmedCustomElementsRegistry;
412-
}
413-
return node.nodeType === Node.ELEMENT_NODE
414-
? ((node as Element).customElements as ShimmedCustomElementsRegistry) ??
415-
null
416-
: null;
411+
const registry = (context as Element)
412+
.customElements as ShimmedCustomElementsRegistry;
413+
return registry ?? null;
417414
};
418415

419416
// Helper to create stand-in element for each tagName registered that delegates
@@ -440,7 +437,8 @@ const createStandInElement = (tagName: string): CustomElementConstructor => {
440437
// upgrade will eventually install the full CE prototype
441438
Object.setPrototypeOf(instance, HTMLElement.prototype);
442439
// Get the node's scope, and its registry (falls back to global registry)
443-
const registry = registryForNode(instance);
440+
const registry = registryFromContext(instance);
441+
registryToSubtree(instance, registry);
444442
const definition = registry?._getDefinition(tagName);
445443
if (definition) {
446444
customize(instance, definition);
@@ -471,7 +469,8 @@ const createStandInElement = (tagName: string): CustomElementConstructor => {
471469
pendingRegistry._upgradeWhenDefined(this, tagName, true);
472470
} else {
473471
const registry =
474-
this.customElements ?? this.parentElement?.customElements;
472+
this.customElements ??
473+
(this.parentNode as Element | ShadowRoot)?.customElements;
475474
if (registry) {
476475
registryToSubtree(
477476
this,
@@ -495,8 +494,8 @@ const createStandInElement = (tagName: string): CustomElementConstructor => {
495494
} else {
496495
// Un-register for upgrade when defined (so we don't leak)
497496
pendingRegistryForElement
498-
.get(this)!
499-
._upgradeWhenDefined(this, tagName, false);
497+
.get(this)
498+
?._upgradeWhenDefined(this, tagName, false);
500499
}
501500
}
502501

@@ -779,36 +778,57 @@ Object.defineProperty(
779778
const creationContext: Array<
780779
Document | CustomElementRegistry | Element | ShadowRoot
781780
> = [document];
782-
const installScopedCreationMethod = (
781+
const installScopedMethod = (
783782
ctor: Function,
784783
method: string,
785-
from?: Document
784+
coda = function (this: Element, result: Node) {
785+
registryToSubtree(
786+
result ?? this,
787+
this.customElements as ShimmedCustomElementsRegistry
788+
);
789+
}
786790
) => {
787-
const native = (from ? Object.getPrototypeOf(from) : ctor.prototype)[method];
791+
const native = ctor.prototype[method];
792+
if (native === undefined) {
793+
return;
794+
}
788795
ctor.prototype[method] = function (
789796
this: Element | ShadowRoot,
790797
...args: Array<unknown>
791798
) {
792799
creationContext.push(this);
793-
const ret = native.apply(from || this, args);
794-
// For disconnected elements, note their creation scope so that e.g.
795-
// innerHTML into them will use the correct scope; note that
796-
// insertAdjacentHTML doesn't return an element, but that's fine since
797-
// it will have a parent that should have a scope
798-
if (ret !== undefined) {
799-
registryToSubtree(
800-
ret,
801-
this.customElements as ShimmedCustomElementsRegistry
802-
);
803-
}
800+
const ret = native.apply(this, args);
804801
creationContext.pop();
802+
coda?.call(this as Element, ret);
805803
return ret;
806804
};
807805
};
808-
installScopedCreationMethod(Element, 'insertAdjacentHTML');
806+
807+
const applyScopeFromParent = function (this: Element) {
808+
const scope = (this.parentNode ?? this) as Element;
809+
registryToSubtree(
810+
scope,
811+
scope.customElements as ShimmedCustomElementsRegistry
812+
);
813+
};
814+
815+
installScopedMethod(Element, 'insertAdjacentHTML', applyScopeFromParent);
816+
installScopedMethod(Element, 'setHTMLUnsafe');
817+
installScopedMethod(ShadowRoot, 'setHTMLUnsafe');
818+
819+
// For setting null elements to this scope.
820+
installScopedMethod(Node, 'appendChild');
821+
installScopedMethod(Node, 'insertBefore');
822+
installScopedMethod(Element, 'append');
823+
installScopedMethod(Element, 'prepend');
824+
installScopedMethod(Element, 'insertAdjacentElement', applyScopeFromParent);
825+
installScopedMethod(Element, 'replaceChild');
826+
installScopedMethod(Element, 'replaceChildren');
827+
installScopedMethod(DocumentFragment, 'append');
828+
installScopedMethod(Element, 'replaceWith', applyScopeFromParent);
809829

810830
// Install scoped innerHTML on Element & ShadowRoot
811-
const installScopedCreationSetter = (ctor: Function, name: string) => {
831+
const installScopedSetter = (ctor: Function, name: string) => {
812832
const descriptor = Object.getOwnPropertyDescriptor(ctor.prototype, name)!;
813833
Object.defineProperty(ctor.prototype, name, {
814834
...descriptor,
@@ -820,8 +840,8 @@ const installScopedCreationSetter = (ctor: Function, name: string) => {
820840
},
821841
});
822842
};
823-
installScopedCreationSetter(Element, 'innerHTML');
824-
installScopedCreationSetter(ShadowRoot, 'innerHTML');
843+
installScopedSetter(Element, 'innerHTML');
844+
installScopedSetter(ShadowRoot, 'innerHTML');
825845

826846
// Install global registry
827847
Object.defineProperty(window, 'customElements', {

0 commit comments

Comments
 (0)