@soyuka/jhaml
v1.0.8
Published
Stream HAML to HTML
Downloads
9
Readme
JHAML
Note: Currently a work in progress, filters are not available yet amongst some features.
Lazy? Jump to usage
Introduction
jHaml stands for Javascript Haml. I'm using scoped packages:
npm install @soyuka/jhaml
Why another HAML implementation?
Because I wanted a streamable HAML implementation. Also, some things didn't work on javascript implementation as they should in comparison to the ruby version (which I was still using with gulp!).
For example:
%div{attr1: 'one',
attr2: 'two'} some content
Errors thrown for no obvious reasons:
%div test
test
What about this syntax:
%div{ng: {click: 'test()', if: 'ok === true'}}
-# Results in <div ng_click="test()" ng_if="ok === true">, the `_` separator can be configured
Also, I almost never need code interpretation inside HAML but I instead write html templates to be used by angular. This is why this parser provides two different streaming engines:
- one that transforms haml to html
- one that transforms haml to javascript which then evaluates to html
Both are implementing the same one-pass
algorithm:
jhamltohtml
has a really low memory footprint and is not executing javascript. It just transforms to HTML and writes data as it comes.jhaml
builds javascript in-memory, executes and then streams the result out.
What's different
It's a stream!
Also, when using code interpretation, the haml code is translated to strict ES6 javascript.
This means:
- it requires node > 4
- it runs with the help of es6 templates
- you have to declare variables
For example with tj/haml.js implementation you was able to do:
- name = 'test'
%p= name
This implementation would require the variable to be defined, mainly because we're in strict mode:
- let name = 'test'
%p= name
You've only access (for now) to basic loops (while, for) and conditions (if, else, switch) but not .forEach
or .map
for bloc operations.
For example this will work:
- let a = [0,1,2].map(i => i+1)
- for(let i in a)
%p= a[i]
You may think a forEach
loop could work, for example:
- ;[0,1,2].forEach(function(e)
%p=e
But this will not be closed properly. If you want to do things like that (you should not need this in a template), it'd be written like this:
- ;[0,1,2].forEach(e => __html += `<p>${e}</p>`)
__html
is a global scope variable in which html is being appended.
And here's how a switch
would be implemented:
- switch(interpolated)
- case 'Test':
%p Ok
- break;
- default:
%p Not ok
Basically, code that get's an indented block will be surrounded by {
and }
. Code that's on the same indentation will just be executed as is.
An if/else
condition is written like this:
- if(disabled)
%p Disabled
- else if(disabled === false)
%p Not disabled
- else
%p Disabled is not defined: "#{disabled}"
You can of course execute you own methods:
- let t = myfunction('foo')
%p= t
-# same result as:
%p #{myfunction('foo')}
Compatibility
This script runs the tj/haml.js test suite except non-standard for each
loops.
This does not mean that it'll produce the same output!
Usage
npm install -g @soyuka/jhaml
Programmaticaly, jhaml
gives you two parameters :
- the scope
- options
For example:
const jhaml = require('jhaml')
const fs = require('fs')
const scope = {foo: 'bar'}
fs.createReadStream('source.haml')
.pipe(jhaml(scope, {attributes_separator: '_'}))
Current available options are:
attributes_separator
(string): a separator for embed attributes. Default to-
.eval
(boolean): Only available with the Javascript engine (ie when using code interpretation). If set to false, it'll output javascript instead of html.
The attributes separator is only used when parsing recursive attributes:
%div{ng: {click: 'test()', if: 'available'}}
will render:
<div ng-click="test()" ng-if="available"></div>
With code interpretation
CLI
Format: jhaml [--eval] [json scope] [output file]
To javascript:
jhaml --no-eval < file.haml '{"foo": "bar"}'
To Haml, pipe output:
jhaml < file.haml '{"foo": "bar"}' > file.html
To Haml, creates a write stream:
jhaml < file.haml output.html
Programmatic
const jhaml = require('jhaml')
const fs = require('fs')
const scope = {foo: 'bar'}
fs.createReadStream('source.haml')
.pipe(jhaml(scope))
Without code interpretation
CLI
jhamltohtml < file.haml > output.html
jhamltohtml < file.haml output.html
Programmatic
const jhaml = require('jhaml')
const fs = require('fs')
const scope = {foo: 'bar'}
fs.createReadStream('source.haml')
.pipe(jhaml.tohtml(scope))
Gulp
See here for the full documentation
npm install @soyuka/gulp-jhaml --save-dev
gulp.task('haml', ['haml:clean'], function() {
return gulp.src(['./client/haml/**/*.haml'])
.pipe(haml({}, {eval: false}))
.on('error', function(err) {
console.error(err.stack)
})
.pipe(gulp.dest('./html'))
})
Express
See here or here with caching abilities
Todo
- filters
- multi line (
|
) - support other attributes forms?
:attr => value
, array value,(type="test")
. needed?