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
Back to the chalk board #105
Conversation
This MR does the following: * the `isArray` operation happens only at the template level * text and attributes never need to brand check for instanceof Hole or isArray * arrays as holes never need nested `isArray` checks neither: DOM or Hole are all they care about * the loop over many items is done once, no matter if first time render or update * the removal of many rows is now back to the fast path * when the template is different, the previous stack is simply trashed, hopefully reducing edge cases shenanigans * both keyed and non-keyed cases have been stress-tested with js-frameworks-benchmark * on node update, if the current node is the same as before nothing happens
This MR fixes #102 and fixes #103 + it provides further hydration hints out of the box. Current changes: * each fagment is demilited by `<>` and `</>` comments: see notes * this is a linear render: values are never looped more than once * this version of *uhtml* is even more Memory friendly: a lot has been refactored to consume and recycle as much as possible * the fragment in fragment issue has been resolved * the array hole in tags has been converted into a fragment case * the PersistentFragment has been refactored to survive edge cases * the performance is either better or the same as before * the Array hole now is a `<!--[N]-->` comment where `N` is the amount of nodes handled * holes are still transparent so that the amount of nodes is still ideal * a new code coverage goal has been reached: 100% of everything, including uhtml/dom * a new test has been written to help out with expectations on the DOM world (browsers) as well as SSR * the SSR story is still to be defined but everything is coming out nicely ... there are fragment hints, array hints, only missing hints to produce a DOM to Template transformer are holes which might land on SSR version only, as it would be ugly to have so many comments in the wild for no reason
Whoa! Amazing! Do we still need to keep the types consistent? (I.E. Can a Hole become a Hole[]?) I really like not having to wrap everything in I'll be monkey testing it shortly. |
It survives 500 random changes (insert, delete, move, undo, redo, etc) followed by undoing them all to get back to the original configuration on my most complicated design. A quick test answers my question about consistency. If I allow a Hole to become a Hole[] it breaks. Easy enough to avoid. |
again, the contract with arrays is: once array, always array ... the contract works the other way around: once a hole, always a hole ... this should be simple to think about or digest ... but ... The current logic allows you to pass a hole and, after that, a hole that contains an array: const holeInHole = value => html`${value}`;
// this works
render(document.body, holeInHole('a'));
// this also works
render(document.body, holeInHole(html`a`));
// and this works too
render(document.body, holeInHole(html`${'a'}`));
// so that this can work as well
render(document.body, holeInHole(html`${[
html`a`,
html`b`,
]}`)); Ths hole becoming an array out of a sudden won't work but we're back to the contract: once array, always array so that once hole, always a hole. If a specific place in the template might ever accept a list of node, use an array there. If there is this weird case where such place is never an array but then life happens and it needs to be one, pass a hole that wraps an array: html`${html`${[]}`}` That should now work out of the box. Did I answer you? |
Excellent! This is much less error prone. Thank you for this amazing work! |
If it helps anyhow to understand the current design, hence logic, each template literal is unique and it's parsed only once. If the interpolation is an array in any place of the template, and such array is part of the content, not an attribute value, that place in the content is considered mutable, not in the sense that interpolations don't mutate the DOM already, but in the sense that such place is a special one for list of items operations. Then there is the hole primitive, which is a much more convoluted thing to deal with, but if it's used to just carry more complex values not previously meant, it can represent a persistent fragment instead of a regular TEXT or ELEMENT node, and withing that persistent fragment all the same rules apply: if that hole (fragment) has a hole thta it's an array, we're back to the contract and the only placeholder it needs around is a node, in this case a persistent fragment unless wrapped in |
if anything, this conversation made think to export a export const holed = (value, xml = false) => (xml ? svg : html)`${value}`; That would avoid parsing at all for the very same template literal returned by that function so that creating random fragment holes would be also faster out of the box (and dare I say debugging might be also simplified if used in the wild). The details are evil though ... if used as template literal it will backfire so I need some more convoluted logic or a way o disambiguate upfront html VS svg. const holey = (value, xml = false) => (xml ? svg : html)`${value}`;
export const holed = (template, ...values) => (
Array.isArray(template) && values.length ?
holey(values[0]) :
holey(template)
); But then again, we're back to |
Wow. The injected comment nodes are actually useful now! I can easily see that a thing is an array and how many there are. It also feels like there are fewer comments (though I hardly understand why people care). |
This MR fixes #102 and fixes #103 + it provides further hydration hints out of the box.
Current changes:
<>
and</>
comments: see notes<!--[N]-->
comment whereN
is the amount of nodes handledNotes
All fragments, which are those templates used just to propagate other stuff, including arrays or holes or whatever, are now delimited via
<!--<>-->
and<!--</>-->
comments.PersistentFragment automatically handle and deal with those cases internally and that's mandatory to have fragments boundaries.
All arrays pinned comments will show at runtime the amount of nodes they are dealing with:
<!--[0]-->
, as well as<!--[10000]-->
are all valid pinned comments.For the SSR story and hydration done well, holes are the only missing bit and these will be eventually wrapped around a
<!--{}-->
and a<!--{/}-->
comment.Once that lands in uhtml/dom or better, uhtml/ssr, hydration will be a piece of cake and it will be possible to transform the DOM into mapped template + interpolations entries without ever needing to trash a single DOM node that is already live.