Jhaml, yet another javascript haml implementation

yushenwv 8年前

来自: https://github.com/soyuka/jhaml

JHAML

Note: Currently a work in progress, filters are not available yet

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.

For example:

%div{attr1: 'one',     attr2: 'two'} some content

Or errors thrown for no obvious reasons:

%div test    test

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 withtj/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 thetj/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 - , {ng: {click: 'test()', if: 'available'}} will render ng-click="test()" ng-if: "available"
  • eval (boolean): Only available with the Javascript engine (ie when using code interpretation). If set to false, it'll output javascript instead of html.

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

Need another implementation? Please get in touch.