-
Notifications
You must be signed in to change notification settings - Fork 2k
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
[WIP][CS2] JSX support (Haml-inspired) #4553
Conversation
o 'JsxElement', -> $1 | ||
o '{ Expression }', -> $2 | ||
o '{ INDENT Expression OUTDENT }', -> $3 | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There’s over 100 lines of new grammar rules here. This would be a significant burden to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GeoffreyBooth I hear you, that's part of what made me uneasy about whether this belongs in Coffeescript proper. I took a closer look at @xixixao's approach in #4551 and it's super elegant/lightweight. But it relies on having a known end tag to be able to treat JSX element bodies like specially-interpolated strings. If you want whitespace-indented tags I think you have to go more heavyweight as far as lexing differently while you're inside an element body and then having grammar rules (at least to the extent of representing the indentation structure). I do think there's a demand for the whitespace-indented version of JSX, it's certainly what I wanted (thus the itch-scratching) and I saw your comments wishing for a Jade-like syntax (which is pretty similar to what I've implemented). But I'm inclined to think that @xixixao's version, being less breaking-changes-y, much more lightweight, and just actual JSX syntactically (with interpolated Coffeescript) is what belongs in Coffeescript proper. And then I'm not sure exactly what constitutes maintaining something like this (releasing a separate NPM package? periodically merging in master
/2
?) but I'd think that I could maintain this syntactically sweeter version separately and try to make it visible for those who might prefer it, what do you think?
Yeah, I’m inclined to agree. Actually seeing the proposed syntax for both versions, I find myself unexpectedly preferring the I think the way forward for the HAML-like version is to follow up on #4540 and implement a plugin architecture that could supply sufficient hooks to achieve what you’re doing here, without the code from this PR becoming part of the compiler. I recognize that this would be a significant challenge and I’m not sure if it’s even possible, but it would be a future-proof way of implementing not just this syntax but any other templating languages or other customizations people want that don’t belong in CoffeeScript proper. If you could create hooks that support this as a plugin, then presumably almost anything people would want to do should be possible as a plugin. |
This is not merge-ready (and not necessarily thinking that it should be merged, see below): there are some files included (
JSX_TODO
andjsx_examples/
) that wouldn't be merged but may be of interest and there are some remaining features (fromJSX_TODO
) that I'd like to implement as part of this. But a PR seems like the right place to go into detail about what I've got working and raise some questions. @GeoffreyBooth: will retarget to2
I've avoided React out of an aesthetic repulsion to JSX but decided to check it out this past week. Doesn't seem at all bad in a lot of ways but if I'm going to use it, I want to be able to use it nicely in Coffeescript (the nicest language). I love Haml's whitespace-indented syntax and to me the natural way to embed markup in Coffeescript is along those lines. Pretty and clean. JSXY. Normal JSX syntax (using Coffeescript instead of Javascript inside expressions) is supported as well.
Like Haml, elements are specified using
%h1
syntax, with shorthands forid
andclass
(akaclassName
) attributes, and attributes can be specified using{}
object syntax and/or()
more HTML-esque syntax:At the moment you can't compile the above as-is because line-ending comments aren't supported inside JSX elements - that's high on the TODO list but for now you'd have to strip out the comments.
Element content can be inline or indented. In addition to the JSX
{...}
expression syntax, you can use Haml-style=
syntax (inline or from the start of an indented line, with optional indented expression body) for expressions as well. With Coffeescript's everything-is-an-expression approach, this lets you do things like use a whitespace-indentedfor
loop to generate a list of child elements (as opposed to{ list.map( ...callback returning element )}
, which you can still use (with{}
or=
syntax) if you prefer). To nest child elements, you can use Haml-style elements or JSX tags. Like JSX, other element content is treated as text content.To do conditional rendering inside an element body, you can again use
=
syntax, or{...}
if you prefer. Currently if you use=
syntax you have to indent in such a way that all subsequent expression lines (includingelse
) are indented with respect to the initial (eg= if ...
) line.The React docs also suggest the use of
&&
for conditional rendering - you can do that, but= if
syntax seems just as clean and more readable:You can also use postfix
if
/unless
/for
(ie comprehension) syntax to an extent -- basically if you use JSX tag syntax for these then you're good to go because the end tag is explicit but if you use Haml-style elements then currently it only treats it as postfix if it immediately follows the tag (ie it can't have content/body):So hopefully that's a decent overview of the syntax. I didn't give many examples of JSX-style syntax but it should basically mimic JSX just with Coffeescript in expressions instead of JS. One notable exception as also noted by @xixixao in #4551 is object spread syntax.
As far as how this relates to Coffeescript proper, I'll read some of the other threads that seem to be discussing similar questions but I was sort of figuring the options were (1) merge into
2
despite breaking changes (2) try to have some kind of plugin/option for enabling this within Coffeescript proper, though this does seem hard or (3) maintain this separate from Coffeescript proper. Interestingly, no existing tests (against 1.x) are currently failing, but this would be my understanding of the breaking changes:#id
syntax would break most comments that don't have a space after the#
%div
syntax would break some code that doesn't leave a space after the%
operator<div>
syntax would break some code that doesn't leave a space after the<
operator.class
implicit-div syntax in some places where it shouldn't be ambiguous with chained.prop
syntax. So far I don't think I've introduced anything breaking there but am tempted to make a leading.abc
after a blank line be treated as a.class
elementMy sense (ie my own sensibilities and my understanding of Coffeescript philosophy as espoused by @jashkenas) is that incorporating lots of code (including some breaking changes) just to support a syntax that in a few years may be passe is not the cleanest. However I do feel that this syntax (call it Coffeescript-JSX-Haml or if it's a bad idea to actually use the name Haml then Coffeescript-JSXY) is as Coffeescript-y as you're gonna get with adding JSX. So I wonder what the downsides are of maintaining this separately from Coffeescript proper but (hopefully "officially") promoting it as the (or one) way to use Coffeescript in a React project. Then people with existing (Coffeescript) codebases would just change an NPM dependency, fix any possible breaking changes, and start using the extended syntax? Is the hard part keeping it up to date with Coffeescript proper?
I'd like to go through and comment on the code in detail for @GeoffreyBooth / @lydell / whomever might be reviewing it but that seems premature. And I'll save some of the thoughts on how this might relate to the future of Coffeescript for other threads where this seems to be being discussed, but I basically think if you're gonna add support for this, then make it way nicer than using JSX/Javascript. That was my immediate Reaction and I have to imagine there are lots of other coders out there who may want or have to use React but think JSX is (syntactically, at least) kind of gross and would jump at the chance to use it in a way that's more appealing in the same ways that Coffeescript will always be more appealing than Javascript