re: syntax, I agree, it's stopped me from ever trying React/JSX-based frameworks, which I am sure is an over-reaction.
I have a POC syntax extension (babel parser fork) I named JSXG where I introduced "generator elements" which treats the body of the element as a JS generator function that yields JSX elements.
The simple/naive implementation of just running the generator was okay, but I (perhaps prematurely) worried that it would be not ideal to have the resulting list of child elements be actually dynamic-- as opposed to being fixed size but have false/null in place of "empty" slots and also using arrays for lists made by loops.
So, I also had a transform that followed conditional branching and loops etc. and made a template of "slots" and that resulted in a stable count of children, and that improved things a whole lot.
It's been a while since I revisited that, I should try and find it!
Comparisons below.
Aberdeen:
$('div', () => {
if (user.loggedIn) {
$('button.outline:Logout', {
click: () => user.loggedIn = false
});
} else {
$('button:Login', {
click: () => user.loggedIn = true
});
}
});
$('div.row.wide', {$marginTop: '1em'}, () => {
$('div.box:By key', () => {
onEach(pairs, (value, key) => {
$(`li:${key}: ${value}`)
});
})
$('div.box:By desc value', () => {
onEach(pairs, (value, key) => {
$(`li:${key}: ${value}`)
}, value => invertString(value));
})
})
JSX: <div>
{user.loggedIn ? (
<button
className="outline"
onClick={() => user.loggedIn = false}
>
Logout
</button>
) : (
<button onClick={() => user.loggedIn = true}>
Login
</button>
)}
</div>
<div
className="row wide"
style={{ marginTop: '1em' }}
>
<div className="box">
By key
<ul>
{Object.entries(pairs).map(([key, value]) => (
<li key={key}>
{key}: {value}
</li>
))}
</ul>
</div>
<div className="box">
By desc value
<ul>
{Object.entries(pairs).map(([key, value]) => (
<li key={key}>
{key}: {invertString(value)}
</li>
))}
</ul>
</div>
</div>
JSXG: <*div>
if (user.loggedIn) {
yield <button
className="outline"
onClick={() => user.loggedIn = false}
>
Logout
</button>
} else {
yield <button onClick={() => user.loggedIn = true}>
Login
</button>
}
</*div>
<div
className="row wide"
style={{ marginTop: '1em' }}
>
<div className="box">
By key
<*ul>
for (const [key, value] of Object.entries(pairs)) {
yield <li key={key}>
{key}: {value}
</li>
}
</*ul>
</div>
<div className="box">
By desc value
<*ul>
for (const [key, value] of Object.entries(pairs)) {
yield <li key={key}>
{key}: {invertString(value)}
</li>
}
</*ul>
</div>
</div>
Edit:Come to think of it, I think it may have been <div*>...</div*> or even (:O gasp) <div*>...</div>.
That looks cool!
I think it would be pretty easy to transpile this (or something like it) to code Aberdeen can consume. In fact, it would probably be a lot easier than transpiling for React, as Aberdeen uses immediate mode the `for` problem in your comment below wouldn't be a problem at all, so no return values to worry about.
I'd use something like this myself for larger projects, I think. But asking other developers to use a different programming language just to use your library usually doesn't fly well. :-) Coming to think of it, I find it actually kind of surprising that JSX succeeded.
Ah, the roadblocks are coming back to me.
The first was using </* as the marker for the end tag of a JSXG element. It worked, but it seemed like it wouldn't be too well received as parsers today treat /* as a comment in that spot iirc.
Edit: The other premature concern/feature was the ability to have a for loop NOT render to an array and rather be inline.
Normaly
for (...) { yield <>...</> }
// becomes
[[<>...</>, <>...</>, ...]]
But I added a mechanism using string directives that would inline it: "use jsxg:inline"
for (...) { yield <>...</> }
// becomes
[<>...</>, <>...</>, ...]