hyperviews is a template language that transforms to hyperscript.
Use it as a build tool with any h(tag, props, children) compliant framework e.g. React, preact or hyperapp.
consthv=require('hyperviews')hv("<div id='foo'>{state.name}</div>")// => h('div', { id: 'foo' }, (state.name))npm i hyperviews
hyperviews(tmpl, mode, name, argstr)
tmpl(required) - The template string.mode- The output format. Can be one of [raw,esm,cjs,browser], or if any other value is passed the function is exported as a variable with that name. The default israw.name- The default output function name. The default isview.args- The default function arguments. The default isprops state.
Reads the template from stdin,
cat examples/test.html | hyperviews --mode esm --name foo --args bar > examples/test.js
Use curly braces in attributes and text.
<div><aclass={state.class}href='http://www.google.co.uk?q={state.query}'></a>
My name is {state.name} my age is {state.age} and I live at {state.address}
</div>See more interpolation examples
There are two forms of conditional.
Using an if attribute.
<spanif='state.bar === 1'>Show Me!</span>Or using tags <if>, <elseif> and <else>
<div><ifcondition='state.bar === 1'><span>1</span><elseifcondition='state.bar === 2'><span>2</span><else><span>bar is neither 1 or 2, it's {state.bar}!</span></if></div>if tags can be nested.
The each attribute can be used to repeat over items in an Array.
Three additional variables are available during each iteration: $value, $index and $target.
It supports keyed elements as shown here.
<ul><lieach='post in state.posts' key={post.id}><span>{post.title} {$index}</span></li></ul>produces
h('ul',{},(state.posts||[]).map(function($value,$index,$target){constpost=$valuereturnh('li',{key: (post.id)},h('span',{},(post.title)+' '+($index)))},this))<ahref='http://example.com' onclick=this.onClick>{state.foo}</a>produces this output
h('a',{href: 'http://example.com',onclick: this.onClick,(state.foo))The style attribute expects an object
<pstyle="{ color: state.color, fontSize: '12px' }"></p>produces this output
h('p',{style: {color: state.color,fontSize: '12px'}})The script tag literally outputs it's contents.
<script>import{h,Component}from'preact'importMyComponentfrom'./component.js'</script>This is also useful for recursive nodes, e.g. a tree
<ifcondition=state.children><div><ahref='#{state.path}'>{state.name}</a><ul><lieach='child in state.children'><script>view(props,child)</script></li></ul></div><else><ahref='#{state.path}'>{state.name}</a></if>produces this output
functionview(props,state){return(function(){if(state.children){returnh('div',{},[h('a',{href: '#'+(state.path)},(state.name)),h('ul',{},(state.children||[]).map(function($value,$index,$target){varchild=$valuereturnh('li',{},view(props,child))}))])}else{returnh('a',{href: '#'+(state.path)},(state.name))}})()}The function tag outputs a function, returning it's contents.
Supports name and args attributes.
<functionname='MyComponent' args='x y z'><div>{x}</div></script>produces this output
functionMyComponent(x,y,z){returnh('div',null,(x))}Components are declared with if the tag starts with a capital letter.
<div><MyComponentfoo='bar' /></div>produces this output
h('div',null,h(MyComponent,{foo: 'bar'}))How you structure your app is down to you. I like to keep js and html in separate files so a component might look like this:
- MyComponent
- view.html (The template file e.g.
<div>{state.name}</div>) - view.html.js (The transformed
houtput of the file above) - index.js (Imports the transformed view and exports the component)
- view.html (The template file e.g.
but if you want you could build entire modules in a html file like this:
<script>import{h,Component}from'preact'exportdefaultclassMyComponentextendsComponent{constructor(props){super(props)this.render=viewthis.onSubmit=e=>{e.preventDefault()// ...}}}</script><function><section><formonsubmit=this.onSubmit><inputtype=textname=textvalue={state.text}/><inputtype=textname=descriptionvalue={state.description}/></form></section></function>Compiles to
import{h,Component}from'preact'exportdefaultclassMyComponentextendsComponent{constructor(props){super(props)this.render=viewthis.onSubmit=e=>{e.preventDefault()// ...}}}functionview(props,state){returnh('section',null,h('form',{onsubmit: this.onSubmit},[h('input',{type: 'text',name: 'text',value: (state.text)}),h('input',{type: 'text',name: 'description',value: (state.description)})]))}More examples here
Using browserify?
Then install the hyperviewify transform so you can simply require templates.
const view = require('./my-view.html')
npm i hyperviewify