jsx
jsx copied to clipboard
RFC: An evolved JSX 2.0 proposal
I acknowledge that the old JSX 2.0 proposal (#65) had way too many comments to keep up a discussion and is closed for discussion.
This proposal tries to accomodate many existing issues. It is based on the existing JSX 2.0 proposal by @sebmarkbage.
Guiding principles
- Simpler and closer to existing JavaScript syntax. JSX is an extension of Javascript.
- Incorporate less of the html quirks.
What this proposal isn't
-
No special behavior for control structures like if, else e.g special behavior for
<If>
tags. JSX semantics should only focus on expressing the tree structure of tags and expressions. The logic structure should be dictated by existing JS syntax e.g ternaryx ? y : z
andsomeArray.map
to iterate over arrays. Whendo
expressions make it to ES spec, they can focus on more expressive logic semantics. -
Concerned with how jsx gets transpiled and emitted. This proposal purely focuses on syntax. i.e no special syntax for event handler binding.
- #81 namespaces for passing nested objects (jsx already supports namespaces) - this is emit level behavior
- #122 syntatic sugar for bind
- #104 special sugar for child functions
Issues taken into consideration
The proposal purely focuses on parser level semantics
#4 - Drop HTML encoding in text and attributes.
#21 - Computed attribute names.
#23 - Object short hand notation.
#25, #51, #64 - Drop the need for curlies around attribute values if they're a single literal, or parenthesis.
#35, #8 - Crazy Idea: Deprecate JSXText?
#68 - Make JSX even more like JS
#7 - Comments in JSXElement
#117 - non-breaking changes for attribute values
#108 - Computed attribute names
#103 - Curly braces for attribute expressions are pointless and ugly
#76 - Destructuring
#53 - Remove JSXElement
from JSXAttributeValue
production
The major breaking changes to existing spec
- Parens to specify expressions like JS instead of curly braces e.g
{...}
->(...)
- JSXChildren are either JSXElements, ParensExpression or Literals.
- There is no JSXText. No special entity encoding or implicit whitespace to deal with.
JSX Elements
JSXElement remains as is. No changes here.
- JSXOpeningElement JSXClosingElement -
<div></div>
- JSXSelfClosingElement -
</br/>
- JSXMemberExpression -
<Context.Provider></Context.Provider>
- - JSXNamespacedName -
<hello:world></hello:world>
- JSXFragmentElement -
<> </>
JSXAttribute
- Shorthand -
<div x y>
evaluated like JS object shorthand as<div x=x y=y
, instead of current behavior<div x=true y=true>
. Maps to JS object syntax:jsx('div', {x, y})
. - Spread -
<div ...props>
instead of currentdiv {...props}
. Maps to JS object syntax:jsx('div', {...props})
- Computed -
<div ['hello' + 2]='world'>
. Maps to JS object syntaxjsx('div', {['hello' + 2]: 'world'})
- In essence
x=y
maps to{x:y}
in JS object syntax.
JSXAttributeValue
- Literal -
x="hello"
,x='hello'
,x=2
,x=true
,x=null
- TemplateLiteral -
x=`some ${value}`
- Identifier -
x=foo_ba$r23
- ParensExpression -
x=(1+2)
,x=({key: val})
,x=(<div/>)
- ~~JSXFragment~~ - Removed: Use
x=(<></>)
instead ofx=<></>
- ~~JSXElement~~ - Removed: Use
x=(<div/>)
instead ofx=<div/>
. Typescript to-date hasn't support this production. It's support is lacking amongst diffent tools and is needlesly complex. It can be echieved with just two extra characters. See #53
ParensExpression: (expr)
replaces {expr}
as expression syntax since JS already understands (...)
. For x={{hello:world}}
, {
has double meaning. The first use is to signify expression, and the next use is the object initializer.
x=((((((1))))))
still means an expression, no matter how many parentheses.
JSXChild
- JSXElement | JSXFragment - No changes
- ~~JSXText~~ - Removed. Have to use explicit strings instead e.g
<div>"Hello World"</div>
. Whitespace can be explicitly noted e.g<div> " Hello World " </div>
- Literal | TemplateLiteral | Identifier -
<div> 1 foobar true null "SomeStr" `Hello ${world}`</div>
. Any space separated literals work. - ParensExpression -
<div>(user.isLoggedIn() > 0 ? <Login/> : <Home/>)</div>
- Replaces{}
as the expression with()
- SingleLineComment | MultilineComment -
//
or/* */
Complex Example:
<div>
(users.map(user =>
<>
<User user/>
" "
(user.isPro() ? "Pro" : "Free")
</>
))
<span style=({fontWeight: 400})>`There are`</span>
`${users.length} `
(users.length == 1 ? "User" : "Users")
<script>
// We can have single line comments
/* or
* multiline comments like JS
*/
`
multiline text body can contain all sorts of weird characters <>&"'¢£¥€©®
including new lines with ${user.name} expressions
`
</script>
</div>
@sebmarkbage / @ryancavanaugh just wondering if you have any opinions ^
I wonder who else needs to be pinged to have a serious discussion of pros/cons and viability.
A part of me wants to create a typescript prototype with “jsxVersion: 2” compiler flag.