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

refactor(engine): using global registry for local lwc entries via pivots #2564

Closed
wants to merge 4 commits into from

Conversation

caridy
Copy link
Contributor

@caridy caridy commented Nov 2, 2021

Details

This a POC (a 3rd attempt) of using the global registry in a non-observable ways for authors in order to allow LWC to detect connectness of elements via the custom elements.

The previous attempts were:

  1. using the global registry by registering lwc elements on it automatically, this creates conflicts with customers doing the same, even if they were just registering lwc as web components.

An example of the broken app was reported by Diego, the documentation SPA was loading and registering global elements when needed by a markdown file, as the user navigates throughout the app, but because some of those global elements might have been used by a previous view as a secondary element (an element from an LWC template), the app fails to register the global one complaining that the name was claimed by LWC.

  1. using an iframe to isolated the new entries, this creates other issues with identities of built-in elements when created off of the shadow root attached to the element.

This approach is different, it uses what we call pivot elements. This is the same technique used by polymer's scope registry polyfill.

@salesforce-cla
Copy link

salesforce-cla bot commented Nov 2, 2021

Thanks for the contribution! Unfortunately we can't verify the commit author(s): Caridy Patino <c***@c***.i***.s***.com>. One possible solution is to add that email to your GitHub account. Alternatively you can change your commits to another email and force push the change. After getting your commits associated with your GitHub account, sign the Salesforce.com Contributor License Agreement and this Pull Request will be revalidated.

Copy link
Collaborator

@nolanlawson nolanlawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to see a test to demonstrate the fix. I also added some nitpicks about isUndefined and other @lwc/shared functions.

// We need to install the minimum HTMLElement prototype so that
// this instance works like a regular element without a registered
// definition; internalUpgrade will eventually install the full CE prototype
Object.setPrototypeOf(this, HTMLElement.prototype);

This comment was marked as resolved.

packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
const { PivotCtor, UserCtor } = definition;
return new PivotCtor(UserCtor);
};
window.HTMLElement.prototype = NativeHTMLElement.prototype;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIUI, the @lwc/engine-dom package doesn't modify any globals (whereas @lwc/synthetic-shadow modifies lots). Is there a risk to having LWC get into the business of modifying the global window.HTMLElement here?

(Same question for window.customElements as well.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@caridy I was thinking about this some more... It seems to me that the global patches should ideally go in a separate package. We do the same thing for @lwc/synthetic-shadow. A few benefits:

  • @lwc/engine-dom remains pure, doesn't override globals
  • OSS consumers who don't need this behavior (i.e who are comfortable with WCs living in the same namespace) don't have to get it
  • if/when we can get the behavior we need from the web platform (e.g. scoped custom elements registry), we can remove the polyfill

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine either way... but if you move it to another package, you need to have ways to interact with the internals of that other package, so, it is not really a polyfill, it has to have some backdoor for lwc to register internal stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, the DOM package is about the DOM, and this is about the DOM as well, I don't see why not patching is really a problem for this case since the change is not observable for others.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you move it to another package, you need to have ways to interact with the internals of that other package

We already do this with @lwc/synthetic-shadow; we add some special options to attachShadow to communicate between @lwc/engine-dom and the polyfill.

I don't see why not patching is really a problem for this case since the change is not observable for others

Patching globals is always risky business; it's just the law of unintended consequences. E.g. the browser API may change in the future. On platform we own everything, but off-platform I think we should try to be good citizens and avoid patching globals.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized we are already patching globals in @lwc/engine-dom, and it seems to be for the same reason as this PR (lack of a usable scoped custom element registry):

// Monkey patching Node methods to be able to detect the insertions and removal of root elements
// created via createElement.
const { appendChild, insertBefore, removeChild, replaceChild } = _Node.prototype;
assign(_Node.prototype, {

I guess ideally we should have all these global mutations in the same place? @lwc/scoped-custom-element-registry-polyfill or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah.

packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
packages/@lwc/engine-dom/src/patches/global-registry.ts Outdated Show resolved Hide resolved
caridy and others added 2 commits November 8, 2021 14:24
Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>
@nolanlawson
Copy link
Collaborator

@caridy This PR got massively bitrotted by changes in master (sorry about that). I made an effort to rebase it (a5e247d), but the Karma tests are failing, so there's probably something I missed.

With this PR, are we able to remove the global DOM-patching here?

assign(_Node.prototype, {
appendChild(newChild) {
const appendedNode = appendChild.call(this, newChild);
return callNodeSlot(appendedNode, ConnectingSlot);
},
insertBefore(newChild, referenceNode) {
const insertedNode = insertBefore.call(this, newChild, referenceNode);
return callNodeSlot(insertedNode, ConnectingSlot);
},
removeChild(oldChild) {
const removedNode = removeChild.call(this, oldChild);
return callNodeSlot(removedNode, DisconnectingSlot);
},
replaceChild(newChild, oldChild) {
const replacedNode = replaceChild.call(this, newChild, oldChild);
callNodeSlot(replacedNode, DisconnectingSlot);
callNodeSlot(newChild, ConnectingSlot);
return replacedNode;
},
} as Pick<Node, 'appendChild' | 'insertBefore' | 'removeChild' | 'replaceChild'>);

@nolanlawson
Copy link
Collaborator

Well, I got most of the tests passing (1453edf), but there are still some failures due to some subtle issues with the HTMLBridgeElement.

@nolanlawson
Copy link
Collaborator

With this PR, are we able to remove the global DOM-patching here?

N/m, just confirmed that yes, we can.

@caridy
Copy link
Contributor Author

caridy commented Jun 14, 2022

closing in favor of the rebased PR done by @nolanlawson on #2724

@caridy caridy closed this Jun 14, 2022
@caridy caridy deleted the caridy/node-reactions-pivots branch June 14, 2022 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants