Valhalla began as an attempt to use Teact to create a React based application without using pointy brackets or back-ticks. Rude things that bite the syntax and uglify the landscape.
However, React's interfaces have not been stable over the last few years, and Teact's interfaces needed updating. And the advantages of Mithril over React (patent issues included) pointed to a better direction for evolution.
Halvalla is intended to allow a teacup syntax html scripting notation to run unchanged in any of three back-end rendering engines: React and Mithril virtual DOMs and HTML text.
It started out as Teact, but Halvalla put back in the missing sweetness of the Teacup meta-programming scheme.
The following syntax is supported.Your mileage may vary.
Build React or Mithril element trees by composing functions.
You get full javascript control flow, and minimal boilerplate.
It's also quite simple (not really), just a thin (as possible) wrapper around React.createElement or Mithril's m
Halvalla uses an Oracle object to indicate what works in a given environment. For example, this is from the source for 'halvalla-mithril':
#
Mithril=require 'mithril'
#
# Halvalla.render defaults to HTML via internal teacup rendering engine
# which is smart enough to recognize mithril's quirks
# use Halvalla.render to get server-side, Mithril.render to get client side.
Oracle =
summoner: Mithril #the Greater Power
name: 'Mithril' # his name
createElement: Mithril #he creates without hassle, but requires us to
preInstantiate: true # instantiate the whole virtual DOM before rendering
# React allows lazy instatiation when rendering
getProp: (element)->element.attrs # where does mithril stash this info?
getName: (element)->element.tag
trust: (text)-> Mithril.trust text # how to specify unescaped text
#require the Halvalla engine, but throw away it's default Oracle's tags
{Halvalla} = require '../src/halvalla.coffee'
# create a new Halvalla with new overrides
#export identically to the original Halvalla
module.exports= (new Halvalla Oracle).tags()
module.exports.Halvalla =Halvalla
#
Currently, only the external aspects of Valhalla are in place: Valhalla will get you to Mithril or React with your exact same source code, but the lifecycle methods and class structures of React and Mithril do not get the same universal treatment... Yet
There are three ways to import Halvalla
- set up the environment for React
{tag,...} = require 'halvalla-react'
- set up the environment render to HTML directly with internal rendering
{tag,...} = require 'halvalla'
- set up the environment for Mithril
{tag,div,...} = require 'halvalla-mithril'
Halvalla exports bound functions for elements, giving you options for terser syntax if you're into that:
H = require 'halvalla-mithril'
H.div className: 'foo', ->
H.text 'Blah!'
or the Teacup / CoffeeCup signatures:
{div, text} = require 'halvalla-teacup'
div '.foo', ->
text 'Blah!'
{tag} = require 'halvalla-react'
tag 'div', '#root.container', ->
unless props.signedIn
tag 'button', onClick: handleOnClick, 'Sign In'
tag.text 'Welcome!'
Transforms into:
React.createElement('div',
{id: root, className: 'container'}, [
(props.signedIn ? React.createElement('button',
{onClick: handleOnClick}, 'Sign In'
) : null)
'Welcome!'
]
)
Use it from your component's render method:
{div,Component} = H = require 'halvalla-mithril'
class Widget extends Component
render: ->
div className: 'foo', =>
div, 'bar'
And as a surprise, the tests included odd usage like this, and it works.
{tag} = H = require 'halvalla-react'
module.exports = (props) ->
tag 'div', className: 'foo', ->
tag 'div', props.bar
Teacup signatures are updated when the component is instantiated,
or optionally, before instantiation by using Halvalla's bless
.
After that, the new component is bound to Halvalla's knowledge of components, and may be called from other places in your code as properties of Halvalla's exports.
For example in a React based system you might write:
H = require 'halvalla-react'
H.bless class DooDad extends H.Component
render: ->
H.div className: 'doodad', =>
H.span @props.children
# blessing the class makes it visible everywhere as a property of halvalla
And in another module:
H = require 'halvalla-react'
class Widget extends Component
handleFiddle: =>
# ...
render: ->
H.div className: 'foo', =>
H.DooDad, onFiddled: @handleFiddle, =>
H.div "I'm passed to DooDad.props.children"
#Widget will not appear as a property of H _until_ it's first instantiation.
If you need to build up a tree of elements inside a component's render method, you can
escape the element stack via the pureComponent
helper:
H = require 'halvalla-react'
Teas = H.pureComponent (teas) ->
teas.map (tea) ->
# Without pureComponent, this would add teas to the element tree
# in iteration order. With pureComponent, we just return the reversed list
# of divs without adding the element tree. The caller may choose to add
# the returned list.
H.div tea
.reverse()
H.bless class Widget extends H.Component
render: ->
H.div Teas(@props.teas)
Markaby begat CoffeeKup begat CoffeeCup and DryKup which begat Teacup which begat Teact which evolved into Halvalla
$ git clone https://github.com/jahbini/halvala && cd halvalla
$ npm install
$ npm test
- finish mithril tests
- enhance readability
- add adaptors for lifecycle methods
- extend the ability of Halvalla to reduce porting efforts of useful third party components for Mithril and React.