Crystal -> JS Transpiler

The underlying implementation does not use macros, which makes them much more flexible and easier to debug. The only macro we have is for generating the methods so we don’t have to do it by hand. The basics of the implementation can be found here: https://github.com/luckyframework/lucky/blob/4a2a528388b4ae3a229715dbc48f0a98ca7a5882/src/lucky/tags/base_tags.cr#L8

It generates various methods for each tag. Such as one that accept blocks, content, strings, etc.

1 Like

Crystal -> JS is just one part of the problem. Then you need to bind to the DOM and all of the browser API.

Most of web apps are UI apps. React.JS and similar frameworks - abstract DOM away almost completely. Working with React.JS I forgot when was the last time I need to use Browser DOM API explicitly. So you can go very far and cover most web use cases by providing only React-like framework without full JS interoperability and without DOM API.

And it would also allow crystal to build cross-platform UI via Electron.

P.S. For the practical reasons - currently I guess TypeScript is the best opinion.

1 Like

Electron in 2019 lul

For practical and type safety reasons, I think Elm is the best option. I really suggest people play more with this wonderful language. It’s different (functional) but it’s so simple to get things done and to grow your app over time.

Speaking of functional languages… I tried Elixir and other functional languages but my mind can never wrap my head around them.

I think the only way I could learn is if I see code examples from the languages I already know, and how they are created in a functional way.

For example, take:

class Player
	property name = "Default Player #1"
	def initialize
    	pp self.name, " was created!"
	end
end

p = Player.new

No idea how this would be created in a functional language. I have read Elixir is used by some AAA game companies for servers, which is really cool.

…and I don’t even want to get into parameters and how they are passed. Reference or Value? Depends on the type? If allocated on heap vs stack?

In case you’re not set on browser environments only, native might be another option:
-> Is this a road to some crystal-to-all-platforms rails?

Are you thinking features along the lines of meteor js?
A full-stack ruby alternative to the https://hood.ie javascript stack, and to the separate https://elm-lang.org/ frontend programming language (https://blog.crisp.se/2018/03/08/perlundholm/elm-for-backend-developers) or to the https://phoenixframework.org backend with its liveview frontend generator.

It wound then likely need to include disconnected operation / re-sync / offline-first these days.
Something like next version of PouchDB in the browser (Or an own db sync connector on the server providing user-individual 2-way pub/sub/sync data shares?)
The problem: https://stackoverflow.com/questions/46351432/per-user-db-pouchdb-couchdb-shared-data-doable)

Another idea?:

What about doing it the other way around. Using for example, the http://voltframework.com and adapting it to compile the web-backend with crystal, and maybe even platform binaries?

There is even a funding offer for getting “volt” re-born:

Are you thinking along the lines of meteor js

Yes, but more exactly along the lines of hyperstack (see https: “Hyperstack” dot Org) which is based on the ideas of meteor and volt.

The difference between meteor is that its Ruby, not JS. And the difference between Volt is that it uses React.js under the hood on the client, and ActiveRecord models (on the server and client) instead of MongoDB.

So what the hyperstack core team is looking to do is port Hyperstack from Ruby to Crystal, but that requires a Crystal -> JS transpiler. Once we had that I believe the rest is straight forward.

Having Crystal on the server and Ruby on the client defeats the purpose btw. You would then have to write API adapters on either side of the wire, context switch between programming languages and tool chains etc.

2 Likes

I think there is lots of confusion still here.

The HUGE advantage of transpiling to JS (rather than compiling to WASM) is that as long as you do the mapping between JS functions and crystal methods smartly, you already have the DOM bindings.

This is the approach used by the Opal Ruby transpiler.

1 Like

Why would you need DOM bindings? When none of modern web UI frameworks using it. React, Vue, Angular - it’s all higher level abstraction. Browser / DOM API is just an ugly low level thing that pretty much nobody these days uses directly.

P.S.

For Opal it would be nice if it had proper async/await (or Fibers) support.

There are still occasions even with those frameworks to reach in and manipulate the DOM. However the point is that by transpiling to JS you can interface to any JS code, whether low level (DOM) or high level (React or Vue)

As far as async/wait goes: Those provide some syntactic sugar on top a lower level promise mechanism. This is useful in JS, because you can’t really build a decent Promise DSL. In Ruby the promise class gives just as nice code as async/wait, without all the crazy caveats, and inconsistencies of async/wait.

I should do a blog post on this :-)

3 Likes

Found this listing DOM transpiling feature descriptions:

@catmando I didn’t find anything about hyperstack and offline operation, aka “progressive web apps (or PWAs)”, and re-syncing.

Does hyperstack support working with react’s PWA feature enabled?
Does the isomorphic code (and the models) re-sync well after offline periods?

I can’t speak for Vue and Angular, but React exposes DOM events and nodes (via refs) so you can’t really escape the DOM without a framework transforming those events and nodes into a different abstraction.

Plus, you probably want to provide bindings to other browser APIs like WebSockets, XHR/fetch, IndexedDB, ServiceWorker, geolocation, or Promises, so you’d need Crystal bindings for those, as well. :-)

The Clearwater framework is somewhat React-like and uses the bowser gem to handle its Ruby bindings for the DOM and other browser APIs, which I’ve used in several Clearwater apps.

Sorry for the delayed response. Probably best to continue to conversation at the Hyperstack site, as it really is not relevant to Crystal.

https://join.slack.com/t/hyperstack-org/shared_invite/enQtNTg4NTI5NzQyNTYyLWQ4YTZlMGU0OGIxMDQzZGIxMjNlOGY5MjRhOTdlMWUzZWYyMTMzYWJkNTZmZDRhMDEzODA0NWRkMDM4MjdmNDE

I can do all those things with Headless Browser emulator, without accessing the actual Browser API.

All I need inside of the Crystal Black Box are two functions browser.eval(js-code-as-string) -> response-serialised-as-string and browser.on(event-name-as-string, callback).

Using these two methods you are able to take full control over Browser and do pretty much anything, without any special api. Yes, the performance will be not good, but, the modern computers and mobile phones are so insanely fast that it will be totally fine for like 90% of apps.

You can do this in client-side code?

@jgaskins you already did something like that in Phoenix-style LiveView ;)

With LiveView it worked like that

- Browser
- HTTP communication
- Crystal Server with LiveView on remote Machine

But it also could work this way, and run everything in Browser, no need to implement any wrappers for Browser API.

- Browser
- stdin/stout communication with Crystal WASM in same Browser
- Crystal Server with LiveView compiled to WASM and run in same Browser

http://repl.it has Crystal REPL with Crystal compiled to WASM and stdin/stdout. So it should be possible to do all that.

The example from that topic the LiveView real estate Listing - could be run with the Crystal Server compiled to WASM and running inside of the browser. Without any access to Browser API.

2 Likes

Do you have any leads on how to compile Crystal to WASM?
It would be great to code in Crystal WASM instead of Rust or Go in the browser!