Skip to content
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

Meta: Support JSX similar to Literate CoffeeScript? #4529

Closed
GeoffreyBooth opened this issue Apr 25, 2017 · 50 comments
Closed

Meta: Support JSX similar to Literate CoffeeScript? #4529

GeoffreyBooth opened this issue Apr 25, 2017 · 50 comments
Labels
Milestone

Comments

@GeoffreyBooth
Copy link
Collaborator

I’ve been thinking about JSX, and a way that CoffeeScript could perhaps support it. In particular, I’ve been thinking about how it’s actually somewhat similar to Literate CoffeeScript:

  • Literate CoffeeScript is essentially a Markdown file that uses indentation to define CoffeeScript code blocks.
  • JSX is JavaScript that uses XML open and closing tags to define HTML blocks (that themselves can contain JavaScript, within braces).

I haven’t used JSX in a project, so if I’m oversimplifying here, please correct me.

What if the CoffeeScript compiler detected XML open and closing tags and treated them the same as backticks? In other words, everything from the first < of the first opening tag through to the last > of the last closing tag would be passed through unmodified, similar to embedded JavaScript. The compiler would therefore be outputting JSX, and then the next step in the build chain would be Babel with the JSX transform.

This wouldn’t even be a breaking change. Currently <div> throws unexpected <, and </div> throws missing / (unclosed regex).

We also wouldn’t be favoring one framework over another, because we’re just outputting the straight HTML, not converting it into code like React.createElement. Vue has added support for JSX, and the Babel transform is now generic to allow future frameworks to adopt it, so it’s not a React-specific thing anymore.

How is this different from coffee-react or coffee-react-transform or CJSX, you might ask?

  • That project is deprecated, for starters, and is pinned to version 1.9.1 of the compiler.
  • It didn’t rely on Babel, compiling straight to the final JavaScript React.createElement output, and therefore was dependant on React; the last version supports “React >= 0.14.x”.
  • It aimed to compile CoffeeScript within the XML blocks.

That last point is important. In my proposal, within an XML block you would need to use this.foo, not @foo. You couldn’t write code like coffee-react‘s example {<p key={n}>This line has been printed {n} times</p> for n in [1..5]}—you would need to write the JavaScript equivalent. But I don’t think this is so terrible. In my experience, most of the JavaScript inside a template is very minimal, generally just references to objects or basic constructs like if statements or loops. (Again, please correct me if I’m wrong.) It would be a formidable challenge to parse CoffeeScript within XML tags, which is probably why coffee-react was deprecated; but I don’t think we need to take on that task.

I can certainly see people objecting to this proposal on the grounds that people shouldn’t be forced to use JavaScript, or even snippets of it, within a CoffeeScript file. Fair enough. Another perfectly valid objection is that JSX isn’t hard to achieve right now using plain backticks:

getGreeting = (user) ->
  if user
    `<h1>Hello, {formatName(user)}!</h1>`
  else
    `<h1>Hello, Stranger.</h1>`

var getGreeting;

getGreeting = function(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  } else {
    return <h1>Hello, Stranger.</h1>;
  }
};

See CoffeeScript and Babel conversions. If we don’t implement this proposal, at the very least we should perhaps at this example to the docs, either in the backticks section or in a new section discussing JSX.

But anyway, now that JSX is growing into a framework-independent standard, and we can support it in a minimal, framework-independent way, should we? And if we should, does this seem like the way to add support for it?

@mrmowgli
Copy link
Contributor

mrmowgli commented Apr 25, 2017 via email

@GeoffreyBooth
Copy link
Collaborator Author

@mrmowgli What I’m proposing would work with Handlebars or Ember. That’s the point of passing through HTML blocks as is: it’s independent of any particular templating system.

greet = ->
  <h1>Hello, world!</h1>

would become

var greet;

greet = function() {
  return <h1>Hello, world!</h1>;
};

JSX is just a file format, that defines HTML blocks with JavaScript; similar to how Literate CoffeeScript defines CoffeeScript blocks within Markdown. We would leave it to other steps in the build chain (i.e. Babel with various transforms) to parse the HTML blocks into specific precompiled templates, whether they be React or Vue or Handlebars or something else.

@lydell
Copy link
Collaborator

lydell commented Apr 25, 2017

If we're going to support JSX, we should parse it for real. No "passing strings through" hacks.

I think it would make sense to output JSX as well, and let users pass that on to other compilers.

Here's a common example of more complicated JS embedded inside JSX:

<Table>
  {items.map(item => {
    const itemId = item.is_accessory ? item.parentId : item.id;
    const hidden = !allHidden && removedItemIds.indexOf(itemId) >= 0;
    return (
      <ItemRow
        key={item.id}
        item={item}
        quantityOverride={itemQuantityOverrides[itemId] || null}
        className={hidden ? 'js-Hiding is-hidden' : undefined}
      />
    );
  })}
</Table>

@jashkenas
Copy link
Owner

This has come up before. And I think that we definitely shouldn't add halfassed JSX support to CoffeeScript. Both because if you're going to parse it you should be parsing it for real; and because JSX, as a syntax, is almost diametrically opposed to the spirit of CoffeeScript.

@GeoffreyBooth
Copy link
Collaborator Author

@jashkenas I think you’re a little too harsh. This proposal is “halfassed JSX support” in the same way that backticks are halfassed JavaScript support or that Literate CoffeeScript is halfassed Markdown support. We already have established patterns, in Literate CoffeeScript and backticks, of passing through code to be compiled by other components of a build chain. Just because XML syntax is ugly isn’t a great reason to exclude it as an option.

These XML blocks don’t necessarily have to include JavaScript. Vue uses JSX to embed Vue templates:

new Vue({
  el: '#app',
  data: {
    msg: 'Click to see the message.'
  },
  methods: {
    hello () {
      alert('This is the message.')
    }
  },
  render: function render(h) {
    return (
      <span
        class={{ 'my-class-3': true }}
        style={{ cursor: 'pointer' }}
        on-click={ this.hello }
      >
        { this.msg }
      </span>
    )
  }
});

From this precedent, I can easily see someone extending the Babel JSX transform to support Handlebars and other similar templates. JSX at this point is becoming another hybrid passthrough format like Literate CoffeeScript.

@jashkenas
Copy link
Owner

Backticks are halfassed JavaScript support, and you're not supposed to use them. They're an emergency escape hatch in times of need.

Literate CoffeeScript is on slightly firmer ground insofar as that it's not trying to mix two different programming languages, with the inter-nested back and forth that requires. It's just top-level prose and code. Let's call it 2/3 assed.

@emilebosch
Copy link

Can't we add hooks to the compilation stage to add flavors AKA babel presets?

@jashkenas isn't there some way that we can extend coffee script with JSX. It has always been we're not going to do it, but what IS possible/acceptable for you?

@GeoffreyBooth
Copy link
Collaborator Author

Another approach could be to have a preprocessor like @billymoon’s Illiterate, that detects XML blocks and just wraps them in backticks before passing that to the CoffeeScript compiler, which would then pass to Babel with a JSX transform. Such a preprocessor could even theoretically try to compile CoffeeScript to JavaScript within the XML blocks, if it were ambitious.

I don’t think the “wrap XML blocks in backticks” workaround is so bad, personally. If we’re not going to do any other alternatives I think we should at least document it.

More to the point, though, if we want people to use CoffeeScript it needs to integrate well with popular JavaScript frameworks and the JavaScript ecosystem. Say what you will about JSX, and I agree its very concept is a somewhat hideous idea, but like it or not it’s a significant part of the JavaScript ecosystem now. It’s a reasonable question for people to ask how to use JSX with CoffeeScript, especially since coffee-react has been deprecated. I’m not saying the answer needs to be this proposal, or backticks, or some as-yet-unbuilt preprocessor, but the answer shouldn’t be to just diss JSX as stupid and walk away.

@mrmowgli
Copy link
Contributor

mrmowgli commented Apr 25, 2017 via email

@xixixao
Copy link
Contributor

xixixao commented Apr 30, 2017

The power of JSX is that it is a very thin layer on top JavaScript, and you can use full power JS within it and around it. This would only make sense if the JSX support in CoffeeScript was proper:

return
  <span
    class={'my-class-3': true}
    style={cursor: 'pointer'}
    onClick={@hello}>
    {@msg}
  </span>

from your example. I agree that it is very difficult to write a valid CoffeeScript expression that would include JSX, so this could be a non-breaking change. I also agree that the output should be JSX for another preprocessor.

I would also trade x = a<div being invalid CoffeeScript if it helped make the JSX support easier to implement.

I get that JSX seems against CoffeeScript philosophy. It's unnecessarily verbose. But it has evolved to more than it was a couple years ago. It's a compatible syntax which allows different compilation+execution strategy (in React it's essentially a lazy function call).

As such it doesn't really matter that it looks like XML, and I wouldn't mind dropping that (if there was a tool ala js2coffee that would allow easy migration between the two). But you still need syntax that allows "special function calls with named arguments and one unnamed children argument" to meet feature parity (dropping any of these attributes makes it such a small win that it's better to drop to normal CoffeeScript function call syntax with a special naming strategy, like _div here ).

You could try for example:

return
  <div a: 3 b: "x" d: {a: 3} e: [1 2 3]>
    "we might not need the {} around CS expressions"
    <div>
      <Component />

(note that CSX can be simpler because we can rely on indentation, which JSX cannot)

All this said, it would be awesome to have some JSX/CSX support in CoffeeScript. React is hugely influential for client-side (compare Github stars for Ember or handlerbars) and most of today's JS (ECMAScript) transpilers support JSX.

@GeoffreyBooth
Copy link
Collaborator Author

GeoffreyBooth commented May 5, 2017

Can anyone familiar with the lexer explain why this doesn’t compile?

a ` = ` 1

[stdin]:1:9: error: unexpected number
a ` = ` 1
        ^

I ask because it would make possible code like this:

greet = ->
  `<h1>Hello, ` @user `!</h1>`

@lydell @xixixao

@williamstein
Copy link

At my company we write code in CJSX pretty much all day every day (in https://github.com/sagemathinc/smc). So huge +1 to this proposal. We'll surely use what you do; I'm obviously nervous about relying on the deprecated current cjsx project...

@GeoffreyBooth
Copy link
Collaborator Author

@williamstein anyone at your company willing to help code this? 😉 I have an idea for how to approach it, I’m hoping someone answers my question above . . .

@connec
Copy link
Collaborator

connec commented May 5, 2017

@GeoffreyBooth I don't know the particulars of why the above doesn't work, but as an interesting data point it does work if you wrap the backticks in parens, but it compiles to a( = (c)). I suspect it might not be directly related to the lexer, but rather that the grammar context for backticked expressions is restricted to places where an arbitrary expression can occur.

@GeoffreyBooth
Copy link
Collaborator Author

GeoffreyBooth commented May 5, 2017

Yeah, this doesn’t work either:

a ### = ### 1

[stdin]:1:3: error: unexpected  = 
a ### = ### 1
  ^^^^^^^^^^

Very odd.

@williamstein
Copy link

anyone at your company willing to help code this?

Unfortunately not; none of us have much expertise in parsers, etc... We're obviously interesting in testing.

@vendethiel
Copy link
Collaborator

Because bacticks aren't implicitly callable.

$ coffee -bce "f `g`, h"
f(g, h);

@GeoffreyBooth
Copy link
Collaborator Author

@vendethiel I don’t understand your comment.

I also just came across this, which might be related: #4290.

@vendethiel
Copy link
Collaborator

Your

a ` = ` 1

Can't work because the space after a starts an implicit call. The space after the 2nd backtick would as well, if bacticked expressions had implicit calls.

@GeoffreyBooth
Copy link
Collaborator Author

Okay, but then how do you explain this?

a` = `1

[stdin]:1:2: error: unexpected  = 
a` = `1
 ^^^^^

It’s the same error as when putting an inline block comment here.

@GeoffreyBooth GeoffreyBooth changed the title Support JSX similar to Literate CoffeeScript? Meta: Support JSX similar to Literate CoffeeScript? May 6, 2017
@vendethiel
Copy link
Collaborator

vendethiel commented May 6, 2017

The lexer gives IDENTIFIER JS NUMBER, which is not matched by any rule in the grammar.

It's the same reason why we don't allow comments everywhere -- we have a few issues about this: we lex block comments (because we need to re-output them) as HERECOMMENTs, which is the grammar rule Comment. That rule is only allowed in two places.

@GeoffreyBooth
Copy link
Collaborator Author

So I guess the issue is that really my example is too simple. It seems obvious how the compiler should output a ` = ` 1, because it’s a literal match of the JS. But when there’s significant rewriting going on, like eat food for food in foods when `/* string */` food isnt 'chocolate', it’s ambiguous where the embedded JS /* string */ should go.

@GeoffreyBooth
Copy link
Collaborator Author

Getting back to the original goal of this thread, I do still think that there needs to be a way for CoffeeScript to interact with JSX in a manner that’s robust enough for average developers to use. I think there’s no question that many CoffeeScripters hate JSX, both the very idea of its mixing of markup and logic and also the ugliness of its XML-based syntax; but that’s beside the point. The mission statement of CoffeeScript is “it’s just JavaScript.” For CoffeeScript 2 we elaborated on that to point out that nowadays, JavaScript is ES2015+, and so if CoffeeScript is to remain “just JavaScript” it needs to also be ES2015 and its successors. But an even broader point is that being “just JavaScript” means interoperating with the overall JavaScript ecosystem. Whatever we may think of the monstrosity that is JSX, it has grown from a hacky part of React into an emerging standard that is used by multiple frameworks. If CoffeeScript is still “just JavaScript,” it should work with that standard and those frameworks as well as standard ES2015+ JavaScript does.

The specific implementation of how this can be achieved is well worth debating. Seeing what was done in #4551, it seems obvious to me that the shortcut approaches I was hoping for in the ideas above would probably have proven unworkable. Building hooks for a plugin architecture, as discussed in #4540, might make the CSX proposal in #4551 possible, if we add enough hooks throughout the lexer and elsewhere; but I think that that would be adding even more complexity than just building support into the compiler. If the only breaking change is that people need to use spaces around < and >, which most people probably do already anyway for good style, then I don’t think we need a flag or plugin or other way to selectively enable CSX support. Let’s just add it.

@mrmowgli
Copy link
Contributor

mrmowgli commented May 16, 2017 via email

@GeoffreyBooth
Copy link
Collaborator Author

@mrmowgli Read the code in the CSX PR. That would take a lot of hooks. Not that I'm against adding the hooks, but JSX might be too complicated for them. We could also add CSX now and refactor it out into hooks later, like I was hoping to do with Literate CoffeeScript.

JSX is just XML, and as such, isn't really changing. We would have to worry about changes if we were doing what coffee-react-transform was doing, compiling XML tags into React function calls, but we would just be compiling XML tags to XML tags and letting Babel handle the React (or Vue or whatever) part. That seems pretty safe.

@greghuc
Copy link
Contributor

greghuc commented May 16, 2017

As per request from @GeoffreyBooth, I'm moving some comments I made over on [CS2] Support for CSX - equivalent of JSX. In short, JSX via template template literals could be the way to go: implemented as a self-contained library, rather than adding support to the core compiler.

Copied comments..

@greghuc said...

For the future of coffeescript, I think having a clear solution for handling jsx is good. However, I'm not convinced that adding compiler-level support is the way to go: it increases complexity, makes future change to the compiler harder, and biases coffeescript towards jsx over other templating approaches.

As a pointer for another direction, a clean jsx solution might be possible using tagged template strings. Consider:

With bel/hyperx an "html template" is specified as the tagged template "string", which is supplied to the tagged template "function" for processing into a dom (or react?) element. I don't use jsx, but I do use bel to construct html dom elements like this in Coffeescript:

    element = bel"""
      <div>
        <h3>Contact Us</h3>
        <form>
            <label for="email">From</label>
            <input type="text" oninput=#{handleEmailInput} value="#{model.email}">
            <textarea placeholder="Some text" oninput=#{handleContentInput}>{model.content}</textarea>
        </form>
      </div>
    """

I find this a very clean approach: the html template is specified as a templated-string, which is post-processed into a dom element tree using the prefixing template-function (bel in this case). This is what motivated me to propose and add tagged template string support to CS2.

So I would suggest that a cleaner solution to adding JSX support to Coffeescript is to find/create a small library that uses tagged template strings ala hyperx/bel. There might even be a library that does this already. Given the current popularity of JSX, the coffeescript docs could then have a section showing use of JSX with Coffeescript using this library. But we wouldn't add first-class support at the compiler level.

Quick addendums:

Then @GeoffreyBooth asked...

@greghuc how would you implement @lydell’s JSX example using tagged template literals?

<Table>
  {items.map(item => {
    const itemId = item.is_accessory ? item.parentId : item.id;
    const hidden = !allHidden && removedItemIds.indexOf(itemId) >= 0;
    return (
      <ItemRow
        key={item.id}
        item={item}
        quantityOverride={itemQuantityOverrides[itemId] || null}
        className={hidden ? 'js-Hiding is-hidden' : undefined}
      />
    );
  })}
</Table>

and @greghuc replied...

@GeoffreyBooth the example would probably look like:

    bel"""
      <Table>
        #{items.map (item) ->
            itemId = if item.is_accessory then item.parentId else item.id
            hidden = !allHidden && removedItemIds.indexOf(itemId) >= 0
            bel"""
              <ItemRow key=#{item.id}
                       item=#{item}
                       quantityOverride=#{itemQuantityOverrides[itemId] || null}
                       className=#{if hidden then 'js-Hiding is-hidden' else undefined}
              />
            """
         }
      </Table>
    """

I've never done this in jsx, but I do regularly use bel with nested (real) dom components to create complicated dom trees. It works very nicely:

  • The tagged template literal means you can nest code blocks inside the "html", ad infinitum
  • If a code block returns a dom element (or array of), hyperx/bel is clever enough to merge this in when assembling the whole dom tree.

@xixixao
Copy link
Contributor

xixixao commented May 16, 2017

@mrmowgli The fact that this now needs to be maintained as part of the language is also advantage of this approach, because it prevents it from getting out of sync the way coffee-react-transform inevitably would have and indeed did. (I do agree it's also a cost and downside)

I don't see a way to do this via hooks. But someone super smart might come up with a way. But I would bet against the odds of that happening. Our syntax and parser are just too complicated.

@greghuc

it increases complexity, makes future change to the compiler harder

True, but it's not too bad. As @GeoffreyBooth pointed out, a plugin system would have been much more complex. Plus CSX gets parsed into normal CS AST.

and biases coffeescript towards jsx over other templating approaches.

I think we can still support template literals. What other approaches are there?

For the rest of your discussion: All those arguments can be applied against JSX. They are not specific to CoffeeScript. Given that people prefer JSX over template literals (it's a bit unfair since later came after former), I would bet that people will prefer CSX over using template CoffeeScript literals. But again, we can have those, and you can use those instead of CSX if you prefer. But even from your example it seems clear that for the particular syntax that CSX provides they are clunkier, so pragmatically I would use them whenever I need something more/different than CSX.

@greghuc
Copy link
Contributor

greghuc commented May 16, 2017

@xixixao I'm all in favour of Coffeescript having support for JSX :-)

However, I'm not in favour of the Coffeescript compiler having first class support for JSX (requiring compiler changes). The approach I've sketched out is JSX support in the form of a self-contained library called via tagged template literals.

So I don't think the comparison is between JSX and template literals. It's between JSX support:

  • via first class compiler support (e.g. your csx pr), or
  • via a standalone library (e.g. using a library built on hyperx and tagged template literals).

JSX support at the compiler level will always have the cleanest syntax vs a standalone library. However, there's not much in it vs hyperx/bel with tagged template literals: you have some extra tagged template literal syntax like:

h"""
  <div>Hello</div>
"""
or
h"<div>Hello</div>"

vs (as jsx)

<div>Hello</div>

Interesting opinion on JSX from the original author of hyperx: choojs/hyperx#2 (comment)

@xixixao
Copy link
Contributor

xixixao commented May 16, 2017

@greghuc Again, it is argument on whether to use JSX at all, instead of template strings (in JS). "some extra" is a huge deal if you are writing thousands upon thousands of lines of JSX. Ergonomics matter.

@xixixao
Copy link
Contributor

xixixao commented May 16, 2017

For what it's worth, I still think that CoffeeScript, as a concept, is made weaker by including JSX than leaving it out. #4551 (comment)

To address @jashkenas's comment, this is the story I've been pondering:

Once upon a time there was a great language with shitty syntax called JavaScript. People used it a lot, in various ways. Over time, people started considering some of those ways as the bad parts. Then CoffeeScript came along, and allowed you to write JavaScript with better syntax, avoiding the bad parts. This is still CoffeeScript today, and it still has value. It runs everywhere.

Bunch of pretty smart people realized that some of the wins from CoffeeScript could be brought to JavaScript, plus some other things that were out of scope for CoffeeScript or we never got around to them.

Now we are in a new age, where more and more people write in this futuristic dialect of JavaScript, with some additions on top (JSX). This dialect is DEFINITELY much better than ES3/5. So is CoffeeScript classic. But that sucks, because now people have to choose between two variations of JavaScript which are both better than the old JavaScript with bad parts, but each have its pros and cons.

Now consider that many shied away from CoffeeScript regardless of its quality. Unless CoffeeScript gets ahead of the curve, and becomes to ES7+ what it originally was for ES3/5, it will die a quick death.

This is CoffeeScript 2. It needs to support all widely used features as they get rolled out in JavaScript and anything else that is popular and easy to do in the ecosystem, like JSX. Because if I am a dev, who is deciding between Babel and CoffeeScript, CoffeeScript needs to be better.

Now this doesn't mean that CSX in the form I implemented it is the way to go. But the language needs some simple way of outputting JSX, otherwise it will never be adopted by the masses of React/Vue/etc. devs. Imho.

@GeoffreyBooth
Copy link
Collaborator Author

@xixixao what you describe is essentially the CoffeeScript 2 mission statement. To quote from that:

CoffeeScript is losing marketshare. That matters because it becomes harder to choose CoffeeScript for a project when fewer people use it, and when there isn’t public support for the language and a community committed to supporting it as the JavaScript ecosystem evolves. Choosing CoffeeScript for a project can also be stymied if another essential piece of the project, especially its framework, is incompatible with CoffeeScript in some way. . . . for CoffeeScript to remain a viable choice for developers, it must keep pace with the JavaScript community. At the very least it must be compatible with most popular frameworks and build tools; ideally it will also be current with the latest approved standard. The mantra of CoffeeScript is that “It’s just JavaScript”—but right now, JavaScript is ES2015.

At the time I was only thinking in terms of ES2015 features that CoffeeScript needed to support ASAP, especially modules and ES classes, with the priority driven by whichever features impeded CoffeeScript’s compatibility with popular frameworks and build tools. But one could just as easily think of JSX as a feature of the JavaScript community, like any ES feature, that CoffeeScript needs to support in order to remain viable. There are some ES features that we don’t need to support in order to preserve compatibility, and among those features let and const top the list; but while JSX isn’t technically required to work with React, for practical purposes it might as well be. And React is popular enough (and influential enough, with Vue adopting JSX a case in point) that supporting it is a necessity for CoffeeScript’s own future viability.

@connec
Copy link
Collaborator

connec commented May 17, 2017

This is a bit more of a general thing than JSX-specific, but:

If we're tying the success of CS to 'keeping up' with the JS people can use, our target is effectively babel and all its plugins, not ECMAScript itself. We see this already with things like public class fields and JSX (which are not in the ECMAScript standard), and even import/export, for which there is no implementation except transpilation with babel.

If our success is to be matching babel like-for-like, investing in a pluggable parser (whatever that even means 😄), or at the very least attempting to produce a standard AST that can be consumed by babel backends, is probably the only way we can hope to do that sustainably.

@lydell
Copy link
Collaborator

lydell commented May 17, 2017

(The latest Edge and Safari previews have support for import/export as far as I know.)

@connec
Copy link
Collaborator

connec commented May 17, 2017

I also think it's worth bearing in mind what we already support without additional syntax, e.g.:

el = React.createElement

render: ->
  { event, latestLog } = @state

  el 'li', onClick: @handleClick,
    el 'span', className: 'event-name', event?.name
    if latestLog?
      el 'span', className: 'latest-log',
        el 'span', className: 'log-date', latestLog.date.toDateString()
        el 'span', className: 'log-duration', latestLog.duration
        el 'span', className: 'log-percent', "#{latestLog.color}%"
    else
      el 'span', className: 'latest-log empty', 'No logs'

@helixbass
Copy link
Collaborator

helixbass commented May 17, 2017

@greghuc @GeoffreyBooth here's one way you could write @lydell's example in the HAML-inspired syntax I have working (on jsx branch of helixbass/copheescript):

%Table
  = for item in items
    {is_accessory, parentId, id} = item
    itemId = if is_accessory then parentId else id
    hidden = not allHidden and removedItemIds.indexOf(itemId) >= 0
    %ItemRow{
      key: id
      item
      quantityOverride: itemQuantityOverrides[itemId] or null
      className: 'js-Hiding is-hidden' if hidden
    }

I believe this would work the same as the given example. Will open a pull request with all the details but wanted to give a taste and join the discussion on a stylistic level

@phaux
Copy link

phaux commented May 19, 2017

How about a syntax like:

render = (todos) =>
  %ul
    todos.map (todo) =>
      %li
        %input type='checkbox', checked=todo.done
        todo.title

which would be equivalent to:

function render(todos) {
  return <ul>
    {todos.map(todo =>
      <li><input type="checkbox" checked={todo.done}/> {todo.title}</li>
    )}
  </ul>
}

@GeoffreyBooth
Copy link
Collaborator Author

@phaux see #4553

@xixixao
Copy link
Contributor

xixixao commented May 20, 2017

@connec I'm not sure what the big win of outputting to Babel AST would be. The trick is in coming up with CoffeeScript syntax for ES6+ features, then implementing the parsing phase for those. Printing out JS at the end is rather trivial (so lexer, rewriter, grammar are the tricky bits, nodes is pretty easy).

@connec
Copy link
Collaborator

connec commented May 20, 2017

I was being a little facetious tbh. I wouldn't want to see CS become a frontend for babel, but if we did plan to 'compete' with babel feature by feature we could make that intention clearer and the implementation more robust by going through babel's API. Some people want indentation sensitive JS with a few shortcuts, and would feel a bit more inclined if CS was basically a babel parser plugin.

I agree it wouldn't be saving a whole lot of work though. nodes.coffee is certainly easy when compared to the subtleties in the lexer and parser. The larger point is tracking babel, with its plugins and ever growing community, is probably a doomed enterprise without being part of that community.

Definitely a bit off topic for a conversation about JSX though, so sorry about that 😅 I was pretty much just trying to extrapolate from the perspective that JSX is a popular babel plugin.

@stevefan1999-personal
Copy link

Man check this out: https://github.com/jsdf/coffee-react-transform

JSX + CoffeeSctipt = Awesome!

@williamstein
Copy link

Man check this out: https://github.com/jsdf/coffee-react-transform

I've been writing a lot of (production) code using https://github.com/jsdf/coffee-react-transform for the last two years. For example, here is a re-implementation of Juptyer notebooks from scratch, in case you want to see some nontrivial CJSX code (so not just demo examples):

https://github.com/sagemathinc/cocalc/tree/master/src/smc-webapp/jupyter

It is very nice to write....

@GeoffreyBooth
Copy link
Collaborator Author

@williamstein Do you mind trying to compile your project via #4551? It’s a PR that adds JSX support to CoffeeScript 2, and I think it’s ready to merge in. You’ll have to rework your build chain, to run both .coffee and .cjsx files through this PR’s version of the regular CoffeeScript compiler; and then that output through Babel with the JSX transform. #4551 outputs JSX, so Babel is required to turn that into React calls.

It would be helpful to us to see how that PR fares on a real-world project, to see if there are any bugs that need to be addressed. The one caveat we’re already aware of is that sometimes the compiler can be confused by < or > operators without spaces around them, so if you have any i<0s or the like and they throw an error, the solution is to change them to i < 0. Most of the time the compiler can deduce whether the < is meant to signal a JSX tag or a comparison operator, so most such examples shouldn’t require any changes.

@DanielRamosAcosta
Copy link

Hi @GeoffreyBooth, I would like to try it too, but how do I install it? With npm i -S xixixao/coffee-script#csx?

@GeoffreyBooth
Copy link
Collaborator Author

That branch isn't even merged into 2, much less on NPM. It you just want to test, you need to clone @xixixao's repo and check out the csx branch:

git clone -b csx [email protected]:xixixao/coffee-script.git

Then copy the lib folder from there into the node_modules/coffee-script folder (or folders, deeper in the tree) in whatever project you want to test this with, replacing the NPM-installed lib folder. This is basically monkey-patching the CoffeeScript compiler installed in your project with this experimental one. When you're done testing, delete node_modules and run npm install again (or undo the replacing that you did).

You'll also need to do the build chain refactoring I mentioned above, as this new compiler outputs JSX, not ES5 ready-to-run JavaScript.

@DanielRamosAcosta
Copy link

DanielRamosAcosta commented Jun 5, 2017

But with the above command it's suposed to install not from npm, but from xixixao's repo. In fact, I tested it in my react project this component and this other with this webpack config and works like a charm by now. I'm trying to turn my ES6 project to CS.

@xixixao
Copy link
Contributor

xixixao commented Jun 5, 2017

Without spread support existing projects are probably not gonna compile, fyi.

@GeoffreyBooth
Copy link
Collaborator Author

Without spread support existing projects are probably not gonna compile, fyi.

Wow, coffee-react-transform added support for object spread destructuring?

@GeoffreyBooth
Copy link
Collaborator Author

Resolved via #4551.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests