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

enhancement/rework of DOM shim (using parse5) to allow for element properties in the future #178

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

briangrider
Copy link

@briangrider briangrider commented Nov 21, 2024

Related Issue

First step to resolving issue 170

Summary of Changes

dom-shim.js:

  1. Rework DOM shim to use parse5 for serializing children and parsing strings for innerHTML getter and setter
  2. Get ShadowDom "closed" mode working
  3. Create declarative shadow DOM to mimic the browser functionality when content is appended to a shadowRoot
  4. Remove template auto-wrapping functionality from HTMLTemplateElement
  5. Add isShadowRoot utility function to check prototype when assessing whether shadowrootmode attribute needs to be added to appended template/content
  6. Add deepClone utility function to be used with cloneDeep(deep) functionality (this just returned the actual element before)
  7. Add getParse5ElementDefaults utility function to generate empty parse5 elements with much less overhead. At least 2-5x performance gain compared to parsing an empty element in a string (ie. '<template></template>')
  8. Add various "TODO" comments for considerations for improvement

wcc.js

  1. Simplify renderComponentRoots - no need for getInnerHTML, innerHTML, elementHTML, elementTree, hasLight anymore
  2. Remove renderLightDomChildren (passes all tests without)
  3. Remove VOID_ELEMENTS (no longer needed by renderLightDomChildren)
  4. Simplify renderToString, similar to renderComponentRoots

Copy link
Member

@thescientist13 thescientist13 left a comment

Choose a reason for hiding this comment

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

Will try and give this a more thorough testing soon, but just a couple quick observations:

  • Since there are no dependency changes, package-lock.json should probably not be changing
  • It looks like the tests failed only because of coverage thresholds, and so I'm fine bumping down that as needed (in this case to 85% would be ok)

@briangrider
Copy link
Author

briangrider commented Nov 22, 2024

Will try and give this a more thorough testing soon, but just a couple quick observations:

  • Since there are no dependency changes, package-lock.json should probably not be changing

Ah, my bad. I must have had it accidentally checked on Sourcetree, just reset the branch and pushed just the relevant files

  • It looks like the tests failed only because of coverage thresholds, and so I'm fine bumping down that as needed (in this case to 85% would be ok)

Yes, I believe on my initial commit I was at around 91+% on function coverage but it looks like it dipped to 89.28% with some of the additional changes. Yes, if we can temporarily drop the threshold to 85% that would be great

Copy link
Member

@thescientist13 thescientist13 left a comment

Choose a reason for hiding this comment

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

OK, coming out of our chats in #177, here's the state of things

  1. We will land feature/issue 177 Replaced deprecated method getInnerHTML with getHTML #171 first
  2. rebase this branch against master
  3. preserve getHTML in the DOM shim and make sure no other APIs are being removed (and we should probably add some test cases here, which I can help with)
  4. I am OK to let the mode fix come in as part of this PR, but let's make sure by adding a test case - support configurable shadowrootmode attribute for <template> tags #65
  5. Looks like this PR would also help resolve or invalidate verify / ensure proper serialization of shadow roots excluding closed shadow roots from getInnerHTML #16 ?
  6. Sanity test in Greenwood (I will help with this once this gets rebased after feature/issue 177 Replaced deprecated method getInnerHTML with getHTML #171 lands)

Added test for create-document-fragment
Other minor tweaks
# Conflicts:
#	src/dom-shim.js
#	src/wcc.js
@briangrider briangrider reopened this Jan 17, 2025
@briangrider
Copy link
Author

briangrider commented Jan 17, 2025

  1. preserve getHTML in the DOM shim and make sure no other APIs are being removed (and we should probably add some test cases here, which I can help with)

I think we should be good here but let me know.

  1. I am OK to let the mode fix come in as part of this PR, but let's make sure by adding a test case - support configurable shadowrootmode attribute for <template> tags #65

This seems to be the last step here on my end. Will have this done soon.

  1. Looks like this PR would also help resolve or invalidate verify / ensure proper serialization of shadow roots excluding closed shadow roots from getInnerHTML #16 ?

Yes, I believe so.

Copy link
Member

@thescientist13 thescientist13 left a comment

Choose a reason for hiding this comment

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

Looking good!

Just left a few comment / questions, but let me go ahead and start doing some downstream testing of this in Greenwood in the meantime.


before(async function () {
const { html } = await renderToString(new URL('./src/index.js', import.meta.url));
console.log('hello', html);
Copy link
Member

Choose a reason for hiding this comment

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

clean up console.log

* Run wcc against two custom elements, one with an open shadow root and one with a closed shadow root
*
* User Result
* Should return the expected HTML output based on the shadow root mode of each component
Copy link
Member

Choose a reason for hiding this comment

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

Minor suggestion here to maybe get a little more technical / specific on the result here

Should return the expected attribute value for shadowrootmode on the template tag based on the shadow root mode of each component

@@ -0,0 +1,61 @@
/*
* Use Case
* Run wcc against a custom element which creates a document fragment
Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I might have the lost recollection on this, but was there a particular need to test for document fragment specifically? Not sure if the case / result maybe just need to cover a little bit more of the why, maybe that would help jog my recollection?

@@ -133,6 +252,14 @@ class CustomElementsRegistry {
}

define(tagName, BaseClass) {
// TODO Should we throw an error here when a tagName is already defined?
Copy link
Member

Choose a reason for hiding this comment

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

do we need this comment? Seems its similar as the comment below?


import { parseFragment, serialize } from 'parse5';

// TODO Should go into utils file?
Copy link
Member

Choose a reason for hiding this comment

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

re: utils file, if its not needed in other files, it's probably fine to leave. I think there are cases for a util file between wcc and jsx-loader I think, but I think for now we can probably just move the TODO comment, but certainly open to a PR that proposes some code structuring / organization.

}

// Deep clone for cloneNode(deep) - TODO should this go into a utils file?
// structuredClone doesn't work with functions. TODO This works with
Copy link
Member

Choose a reason for hiding this comment

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

Are you suggesting a library would / could handle functions? If we think that feature would be helpful, I would prefer we just extend this to support our needs if so. (would prefer to keep dependencies in this project as slim as possible and already watch to try and better de-couple the JSX ones).

So yeah, if we want to keep iterating on this, I am happy to have an issue / PR to do, and we can clean up the TODO comment.

}

// Creates an empty parse5 element without the parse5 overhead. Results in 2-10x better performance.
// TODO Should this go into a utils files?
Copy link
Member

Choose a reason for hiding this comment

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

same: re utils file comment

@thescientist13 thescientist13 linked an issue Jan 17, 2025 that may be closed by this pull request
@thescientist13 thescientist13 changed the title Rework of DOM shim (using parse5) to allow for element properties in the future enhancement/Rework of DOM shim (using parse5) to allow for element properties in the future Jan 17, 2025
@thescientist13 thescientist13 changed the title enhancement/Rework of DOM shim (using parse5) to allow for element properties in the future enhancement/rework of DOM shim (using parse5) to allow for element properties in the future Jan 17, 2025
Copy link
Member

@thescientist13 thescientist13 left a comment

Choose a reason for hiding this comment

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

So did some downstream testing in Greenwood and I see one regression related to how WCC is handling <html>, <head> and <body> tags it seems, in that it looks like they are being stripped out by WCC now.

For example, this "app layout" component
https://github.com/ProjectEvergreen/greenwood/blob/master/packages/cli/test/cases/build.default.workspace-layouts-page-and-app-dynamic/src/layouts/app.js

export default class AppLayout extends HTMLElement {
  async connectedCallback() {
    const year = new Date().getFullYear();

    this.innerHTML = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>App Layout</title>
        </head>

        <body>
          <h1>App Layout</h1>
          <page-outlet></page-outlet>
          <footer>${year}</footer>
        </body>
      </html>
    `;
  }
}

when run through WCC, doesn't seem to be returning all the content (notice the <title> tag is not wrapped by a <head> tag anymore)

{
  result: {
    layout: null,
    body: '\n' +
      '      \n' +
      '      \n' +
      '        \n' +
      '          <title>App Layout</title>\n' +
      '        \n' +
      '\n' +
      '        \n' +
      '          <h1>App Layout</h1>\n' +
      '          <page-outlet></page-outlet>\n' +
      '          <footer>2025</footer>\n' +
      '        \n' +
      '      \n' +
      '    ',
    frontmatter: null,
    html: null,
    prerender: false,
    isolation: undefined
  }
}

My hunch is that in DOM shim, now that we are pulling in parse5, we are only using parseFragment
https://github.com/ProjectEvergreen/wcc/pull/178/files#diff-d043dc2785231f2ef65d347d7810dae3ca0224e06f777e6d3d66ccc18c5132eeR3

but like in wcc.js we should probably be detecting and using parse or parseFragment
https://github.com/ProjectEvergreen/wcc/blob/master/src/wcc.js#L31

This may be an oversight in the WCC test case, so let's add a new one specifically testing for full document support and I bet it will fail, then we can add the parsing detection, then I bet it will work. 🎯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.16.0 bug Something isn't working enhancement Improvement to existing functionality
Projects
Status: 👀 In review
2 participants