Proposal: ESX as core JS feature

It will be ignored fully, take a look to output, of course.

Again, this is not ESX reaponsibility. You can check tests, the last one, for this case.

So, it's a different approach, and here it will be hanled as in vdom libraries. Notice what root elements still be equal by reference, which is fully equal to .id approach.

For conditional approach it works just like in react, but with additional caching ability.

OK, it's clear you have the full repository/lib in mind but I am afraid I don't ... let's do this instead:
https://webreflection.github.io/udomsay/test/uhooks.html

This is a minimal implementation of a hooked ESX where the code is like:

import {hooked, useState} from 'https://unpkg.com/uhooks?module';

const transform = token => {
  // ad-hoc situation for ESX
};

document.body.appendChild(
  transform(<Counter />)
);

function Counter() {
  const [count, update] = useState(0);
  return count < 10 ?
    <button onClick={() => update(count + 1)}>{count}</button> :
    <div>You reached 10 🥳</div>
  ;
}

Could you show me something live with your code too? You have source and live transformed example from me, at least I can maybe better understand what's your dance there ... and bear in mind I am not using udomsay here, it's literally ad-hoc ESX transformer for that very tiny and specific use case ... would your variant be less than 44 LOC or extremely more performant there? Thanks!

Sooo... here it is - ES ESX example
Code: GitHub - es-esx/simple-example
It's just full copy of your code, with some light changes.

And, of course, on such simple example it cannot be extremely more performant. I think, "static esx" will shine on more complex examples, where distinction between static/dynamic slots and hard caching of full tree will be possible.

Honestly...

Honestly, i was drunk while implementing this example, so it caused really strong issues with understanding it. But this whole idea of ESX is so good, so I don't even bother about it. I really want to push it forward.

I've filed a PR to have same metrics around and running both tests (mine updated too) locally showed basically no difference ... however, this is what I'd like to underline, considering this test/demo is way too trivial to have real-world comparison (hence I am waiting for a udomsay like counter-library that runs js-framework-benchmark):

The type makes switches and comparisons very readable and semantic (no guessing if it's a function is a component or if it's something else go figure out what's that) but thousand repeated typeof doesn't look like possibly a win.

I know the root retention VS just id (which could be a symbol instead of an object, but not all WeakMap accept symbols as key these days) is kinda weak argument, but the point is that yes, my proposal as it is now might do more GC but when components are updated their details can just be collected while a root will always be there (for better or worse).

My ESX always have either same empty array as aither children or attribute, or always the same amount of children and/or attributes, there's no need to invoke anything. I find that convenient and easier for libraries to build around, but also reading your code it looks like getSlotValue checks twice isDynamic via this code and while I understand that's not a real issue, it's double method invocation + double not boolean cast within the two calls + indexOf per each slot can't be really faster, right?
... and that won't happen, or won't be needed, with my tranformer.

As summary: I cannot measure yet benefits of your proposal but I see more moving parts for libraries authors instead of less, hence I am not sure I would personally prefer it but, once again, if this shows benefits with the js-framework-benchmark, and other people believe it's a better way to move ESX forward, I'll follow the crowd without objections :+1:

P.S. forgot to mention the very first line ... with my ESX you just have a token, with your approach you have another method invoke plus you need to check the slots length before even accessing the tag ... again, more moving parts for libraries authors (resulting in also more code to handle your ESX) ... I've just tired to keep DX as simple as possible instead.

I say more - getDynamicSlot value uses indexOf method, that is more worse than just boolean checks :) But i didn't even start optimize runtime besides dynamics gathering at build time.

There is helpers file in esx repo, with simple "bind" implementation, which clones root and make all of it slots static. For basic simple libs it's really simpler to use it, instead of esx instance, but for production grade libs fine understanding of user intents and granular control is pretty crucial.

I agree what DX is really important, but this api is for library authors in first place and not for just developers. It should value performance and flexibility much more higher, I think...

In the end, your approach is closer to JSX origins, and my - to DOM. I'm heading to make real dom lib (based on udomsay maybe?..) so we can look to real showcase and decide which of your points is real, and which just caused by me, simply copypasting and adapting your code :)

yeah, I've edited to mention that too ... an indexOf per each slot doesn't look like a perf win at all indeed.

so far I don't have any evidence, and I believe neither do you ... please find the time to showcase this in the mentioned bigger benchmark ... that's where the fun happens :wink:

on purpose, because the premise of ESX was not to couple it at all with the DOM, as it can be used for any non DOM related tasks too, like it is for JSX already (React Native, SSR, and so on).

Looking forward to see your lib in the wild ... feel free to fork udomsay and start form there re-adapting, adjusting, accordingly to your ESX version. After all that would be the perfect showcase for comparison reasons, so that we can't blame any other smart choice, we have the same library using 2 different ESX ... at least udomsay will be useful as playground, as it was meant to be (but it's fun to write stuff with it, including SSR).

Just came here to say that I think this syntax is wrong even though it is common.

const div = (<div class={'some class'} onclick={() => {}} />)

The {'some class'} for attributes would imply that you define a block with an implicit return of 'some class'. First problem is that in JavaScript implicit returns do not exist in blocks, only in expressions. Second problem is that JavaScript syntax allows you to define blocks inside blocks, so something like this would be valid syntax:

const div = (<div class={
    if (something) { 'some class' } else { 'some other class' } 
} onclick={() => {}} />)

I think this proposal should use a expressions, then it would be consistent with all other JavaScript syntax, regardless of what is common in JSX.

const div = (<div class=('some class') onclick=(() = > {}) />)
1 Like

If we're trying to make this more consistent with how JavaScript works, I think what we would want to do is use the ${...} syntax.

The {...} is simply trying to imply "we are interpolating a value here" and is a fairly common syntax used by many languages including templating languages. Because of that, I don't really have a problem with this syntax. But it's true that JavaScript already has the precedence of using ${...} to mean "interpolate something here", so there is an argument there.

Parentheses on the other hand don't typically mean "interpolation" in either JavaScript, or many other templating languages (though it's not unheard of). They're just one type of thing you can put in a JavaScript expression.

Even this doesn't return(as you said,
> implicit returns do not exist in blocks ) or doesn't work;
we need to use a ternary(?:) if we want it to return:

const div = (<div class={something?'some class' : 'some other class' }  
onclick={() => {}}/>)

If we're trying to make this more consistent with how JavaScript works, I think what we would want to do is use the ${...} syntax.

If not wrong the whole idea is about templating or demarcating expressions
be it:
{..} or
${..} or
{{ }} - like in handlebars
{% %}

And if am not wrong one of main reason for JSX or ESX is to make less cluttered and more readable and more debuggable. Else one could write React Like:

let element = React.createElement('div'); 
let node = .........
.....
node.childNodes.forEach(child => {
    if(someCondition){
        element.appendChild(child)
    else {
        element.appendChild(React.createElement('span'))
    }
})

i.e. more consistent with how JavaScript works

One or two extra symbol($ or % %or {}) for every expression, which add no value, it just clutter to me.

1 Like

for what is worth it, I don't really care if ${...} is used instead of {...} as this is the least important detail of the whole proposal but I agree ${...} would be more aligned with templates, although arguably it could also create more confusion and also make out of the box ESX 100% not compatible with JSX templates, which is the current de-facto standard, hence my choice to not change that part of the proposal. The rest is also not a 1:1 map to JSX classes or syntax, it's an intermediate layer that has nothing to do with vDOM or the DOM in general but anyone can build vDOM or DOM related things on top of it.

2 Likes

Chrome are currently prototyping the DOM parts API. Is there any thought about how that early-stage proposal relates to ESX? Would it make sense for the platform to add both?

I am not involved in that specs and AFAIK it has nothing to do with this proposal ... it's a parallel universe meant to somehow help the template literal story and only for browsers, not ECMAScript, not to improve both FE/BE stories as ESX would ... so ... should they consider both? Yes ... do these have anything in common or to share? No.

1 Like

SX assumes by default a global React.createElement and a global React.Fragment

Implementation should be agnostic to that.

In my QuckJS++ that is a fork of QuickJS of Mr. Bellard i've embedded JSX in JS core.

JSX expression gets compiled into series of JSX(tag,atts,kids) function calls.

And JSX() is aglobal function that cna be redefined. This way JSX can be used with popular reactive libraries like Mithril, PReact, etc. by simple assignment :

JSX = m; // m is a Mithril's function - constructor of vnodes

JSX is not just about markup but also can be considered as convenient way of defining tree data structures, JSON and JS literals can be quite non-readable on deep hierarches.

that screams for global library clashing and conflicts ... it's very sad you decided to go that way, it could've been a massive opportunity for ESX instead which doesn't suffer any JSX issue, as explained and described at length in here and udomsay project ... JSX is not at pair with interpolation, ESX instead is superior and still fully agnostic without encouraging global namespace "war" around who uses how the JSX reference ... conflicts by design, not my cup of tea.

1 Like

I do not see any conflicts in my Sciter, Mithril, PReact and Sciter's Reactor (native built-in implementation of ReactJS ) are happy to work with that JSX implementation.

JSX is not at pair with interpolation

Well, in my implementation the following works :

render <div title=`tooltip ${n}`>Foo</div>;

it works exactly as:

render <div title={`tooltip ${n}`}>Foo</div>;

On the same page you can't share components based on different JSX so that's what I meant ... if you allow to create dozen sandboxes, sure thing ... anyone can land their own thing ... does it compose though?

you didn't get the improvement ESX does there ... add another static attribute to that node and nobody can distinguish between that title being dynamic, and any other prop being static instead, so that vDOM becomes mandatory even for static parts of the template.

JSX wastes and loses a lot of details in its transformation, and the fact your thing works in all JSX based libraries is because they don't have any alternative and JSX is the only thing they know or understand.

ESX is a way forward for many reasons, and compete with template literals without requiring all the awkward issues that template literals have (most notably: highlight and in scope components) so it combines the best of both worlds still preserving its 100% agnostic nature.

Again, sad more people are pushing JSX further when it's old and showed already issues we can keep pretending don't exist but these do.

Best luck for the project though, I am sure in the short term will be successful.

edit in short: can ESX fully represent JSX syntax and transformation? Yes ... can ESX also represent template literals intents? Yes. Can any of the mentioned two current tech-stacks do what ESX does? Sadly No.

1 Like