JSX Support

Posted on
  • I like the Bangle.js Layout library, and I like React. Both are similar, in my opinion. One thing React has which Layout doesn't is JSX: Instead of having to write object literals or function calls, it can be something which looks like HTML or XML.
    For example:

    const layoutObj = <Vertical>
        <Label font="20%">JSX</Label>
        <Button col="blue">Maybe</Label>
    </Vertical>;
    

    instead of

    const layout = { type: "v", c:[
      { type: "txt", font: "20%", label: "JSX"},
      { type: "btn", color: "blue", label:"maybe"}
    ]}
    

    I think it would be cool if the Espruino interpreter supported some form of JSX, since it would make Bangle.js Layout code be easier to read.
    I've made a local branch which implements JSX in the interpreter.
    I think there can be a problem with JsHint not supporting it and stuff, but maybe it can remedied by updating CodeMirror to use ESLint?

  • Wow, that's impressive!

    I'm a bit new to this, but how does JSX normally work? Obviously if I paste this into a browser or Node.js, I get Unexpected token '<' - so does React rely on transpiling this code before upload? Or does it do some weird parsing/code rewriting thing normally?

    I'm a bit iffy about building stuff into the interpreter itself that isn't part of the default JS spec - for instance looking at the changes I believe that the line 1 < /a/.exec("Hallo").index works at the moment but would fail with the new changes.

    At the end of the day, Espruino really isn't anywhere near React, so I think if people start thinking the layout library behaves like it they will ultimately end up confused/disappointed.

    However if we could actually just turn this into a generic XML parser then that could be awesome, and we could build functionality a bit like you're suggesting right in:

    const layout= new Layout(`<Vertical>
        <Label font="20%">JSX</Label>
        <Button col="blue">Maybe</Label>
    </Vertical>`);
    
  • JSX is usually transpiled by the TypeScript compiler or Babel.
    There are multiple issues with my current implementation, I wanted to have a prototype in case it was a definite no.
    A thing React does for some reason is when there's only one child, it's not wrapped in an array in the props. My implementation doesn't do this, because it's weird. I don't think it would make sense to implement it because it would require most things to manually wrap the lone child in case it's a child layouting thing (strings are automatically concatenated). I bring this up because TypeScript's JSX support is very flexible, and it almost support using JSX for layout exactly the way I described, except for that quirk.
    The issue other than RegEx lexing is the fact that the tag contents (inbetween and ) doesn't allow any characters other than the latin alphabet, this is more tricky than it sounds, as there's no way to tell if a string has had single or double quotes after it has been lexed, and it would require manually adding the delimiter characters, which is not ideal. I think the solution would be to have the lexer have flags for a couple of modes:

    • Normal - the usual lexing mode
    • No tokens - always gives one character at a time, without trying to read strings and stuff. Pretokenised characters might be a problem, but they can just be converted into their string forms (It doesn't matter that pretokenised characters represent multiple characters, since it's going to be appended to a buffer string in any case)
    • No regex - this is hacky and less subtle than I personally like, but I don't know of any other way for resolving the issue.

    As per having a string and parsing it, there are multiple pitfalls, some of them which are described in the original JSX spec:

    • There's no way to verify that the XML is correct before running the code.
    • There's no meaningful way of referencing anything which doesn't serialize to a string, eg. there's no way to set a button's callback.

    I see why people would be confused as to why JSX is used in a different way versus React, but using Layout requires reading the docs (or examples) beforehand, so if the usecase of JSX can be explained there, I don't think it will be a problem.

  • Thanks! I'd be interested to hear other people's views on this, but personally I don't see a massive difference in readability between these two:

    const layoutObj = <Vertical>
        <Label font="20%">JSX</Label>
        <Button col="blue">Maybe</Label>
    </Vertical>;
    // ... 
    const layout = { type: "v", c:[
      { type: "txt", font: "20%", label: "JSX"},
      { type: "btn", color: "blue", label:"maybe"}
    ]}
    

    Because the Bangle is so constrained and the screen is small, you're never going to be able to have a lot of stuff in the Layout, so I really don't see a big benefit in this - and actually having a whole new syntax and parser that isn't JS and isn't quite JSX just for a few lines of code in a few apps seems like massive overkill, especially on Bangle.js 1 where we're starting to have to worry about how big the Espruino binary is so it'll fit in the available flash.

    This is undoubtedly cool, but I just think for Bangle.js, it's too much. I guess if you really wanted you could add a plugin to EspruinoTools that detected JSX on the PC, parsed it and rewrote it into a JS object, but I'm conscious that much like the TypeScript support this will probably be something that gets added, used for one app, and then forgotten about.

    A built in XML parser though? That would actually be useful for loads of people - especially with the HTTP request support added to Gadgetbridge

  • Hi! Сorrect the typo, otherwise people won't understand why it's written that way :)
    instead

    <Button col="blue">Maybe</Label>
    
    <Button col="blue">Maybe</Button>
    
  • In fact, I also thought it would be fun if it was possible to use JSX in B.js, but I would also like to have concepts like hooks. In general, I would expect concepts from the React world.

    JSX and React are very popular, this is already a certain standard for the web-dev and if the same concepts could be applied to a microcontroller, then it would be fun :) Also, in theory, it would add popularity to the project, new users, because the world of React is huge.

    However, there must be a person who will do this: create and maintain JSX React transpiler into the Espruino JS code. As a plugin for Babel or something else.

  • I work with React at my job, and I really do love that stuff.
    I don't know if it's relevant, but Preact is very similar to React, and is only like 3kb (with 0 dependencies).

    The way React actually works is by transpilation typescript/bable/webpack magic

    This

    function simplestReactComponent() {
          return <div>Hello world!</div>;
    }
    
    

    Becomes this

    function simplestReactComponent() {
          return React.createElement("div", null, "Hello world!");
    }
    

    It wouldn't under any circumstances be part of the interpreter, but I don't know if it's a good investment of time..

  • I found this tool, it transpiles React live to actual Javascript.

    transpiler

  • I'm worried that using a normal JS library would cause memory problems - in some other posts, it can be seen that the Bangle.js 1 is struggling with memory in some posts in some cases. I think I have to agree with Gordon that the upper limit for UIs on the Bangle.js is not that high to warrant very complex UI frameworks.
    The reason I originally steered away from a master function is because unlike React, where there's one master function for everything (React.createElement, or jsx in newer versions) and having it always loaded everywhere is consented, a lot of Espruino apps might never want Layout (or not have it at all), so I don't think the React way of making elements is suitable for Espruino.
    The way I did it in the intepreter (and what I think should be done) is without a constructor function: the JSX syntax would be a shorthand function call, basically. Esprima natively supports JSX and is used in EspruinoTools, so it would probably make sense to have it convert

    <Button col="blue">Maybe</Button>
    

    to

    Button({ children: "Maybe", col: "blue" })
    

    , though I could see why part-improvised syntax and grammar might be frowned upon.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

JSX Support

Posted by Avatar for g_lander @g_lander

Actions