High performance Javascript template engine inspired by Jade's power and doT's speed.

Jinsoku - template engine

High performance Javascript template engine inspired by Jade's power and doT's speed.

  • Template inheritance
  • Static includes
  • Asynchronous partials loading support
  • Blocks extending and cloning
  • Iterators and Conditionals
  • Extensible ...

via npm:

npm install jinsoku

Jinsoku requires only 2 libs: For Node.js:

  • Async
  • Cheerio

For browser:

  • Async
  • jQuery
<script src="/public/scripts/jquery.js"></script>
<script src="/public/scripts/async.js"></script>
<script src="/public/scripts/jinsoku.js"></script>
  // by default it will look in http[s]://hostname/public/views/ path to read templates 
  // so this will load http://hostname/public/views/home.html 
  Jinsoku.render('home', function(errorcontent) {
    console.log(error || content);

Templates are loaded with jQuery.ajax method by default. Same as on Node.js we can replace Jinsoku.resolve and Jinsoku.template methods with our custom. This make Jinsoku pretty easy to integrate into any web application or framework.

Please note that by default jQuery will execute any <script> that will be found in your templates content.

  • resolve resolve path to template files
  • template read templates content
  • compile compile template into function
  • render compile and run template
var Path = require('path');
var Fs   = require('fs');
var Jinsoku = require('jinsoku');
var root = process.cwd() + Path.sep;
// resolve paths to template files 
Jinsoku.resolve = function(path) {
  return Path.join(root, 'views', path) + '.html';
// get template content from file system, cache or anythig else 
Jinsoku.template = function(pathcallback) {
  var self = this;
  path = self.resolve(path);
  if (self.cache[path]) { callback(null, self.cache[path]); }
  Fs.readFile(path, 'utf-8', function(errorcontent) {
    if (error) { return callback(error); }
    self.cache[path] = content;
    callback(null, content);
var path = 'home'; // this template path after resolving will be: /path/to/app/views/home.html 
// compile template to function 
Jinsoku.compile(path, function(errorfn) {
  if (error) { throw error; }
// render template 
// this will compile template and generated function 
Jinsoku.render(path, function(errorcontent) {
  log(error || content);
  • path: root +'views'+ Path.sep Default root path for templates
  • dataname: data Locals variable object name
  • extract: true Extract data to local scope. If false, locals will be available as dataname.varname.
  • cache: true Cache files content.
  • extension: .html Default template file extension.

To set options we can call:

Jinsoku.set('cache', false);
// or 
Jinsoku.set({ cache: false, extension: '.tpl' });
// or set them as data.options when call render 
var data = {
  options: { cache: false }
Jinsoku.render(path, data, ...);
// as second argument for compile 
Jinsoku.compile(path, { dataname: 'locals' }, ...);

Jinsoku allow different ways to define template.

  • As attributes
// include partial into tag
<head j:include="layout/head"></head>
<head j-include="layout/head"></head>
// each iterator over array
<ul j:each="users :user">
  • As <j> tag
// extend some template
<j extend="footer"></j>
// define a block
<j block="scripts"></j>
  • With square brackets
// simple include
[include: auth/login]
// array iterator
[each:users :user:i]
  #[i+1]. #[user.username]<br>

Jinsoku supports template inheritance via extend keyword.

Suppose we have the following template content.html:

<div id="content">
  <h2>Page title</h2>
  <p>Page content</p>

Now to extend this template in home.html:

<body j:extend="content">
  <div id="copyright">&copy; 2012 MyCompany</div>

and result will be:

  <div id="content">
    <h2>Page title</h2>
    <p>Page content</p>
  <div id="copyright">&copy; 2013 MyCompany</div>

Includes allow you to statically include parts of content.

For example home.html which has head and body in separate files:

<!doctype html>
<html lang="en-US">
  <head j:include="head"></head>
<body j:include="body">
  // or here [include: body]

Each template could be splitted in more parts named blocks.Then after extending this template we may replace content of the blocks, prepend and append content, or even clone them.

Suppose we have this head.html template:

[block: meta]
  <meta charset="utf-8">
<j block="scripts">
  <script type="text/javascript" src="/public/scripts/jquery.js"></script> 

now in home.html to extend and add something to our head:

<!doctype html>
<html lang="en-US">
<head j:extend="head">
  <meta name="keywords" content="Node.js, Javascript, HTML, CSS" j:append="meta">
  <script type="text/javascript" src="/public/scripts/app.js" j:append="scripts"></script> 
  Body content

this will add meta tag to meta block and app.js script to scripts block so final home.html will look like so:

<!doctype html>
<html lang="en-US">
  <meta charset="utf-8">
  <meta name="keywords" content="Node.js, JavaScript, HTML, CSS">
  <script type="text/javascript" src="/public/scripts/jquery.js"></script> 
  <script type="text/javascript" src="/public/scripts/app.js"></script> 
  Body content

To replace a block we need just to redefine it:

// replace scripts block
<head j:extend="head">
  [block: scripts]
  <script src="/public/scripts/main.js"></script> 
// now scripts block will contain only main.js script 

Jinsoku also supports friendly iterators over arrays(each) and objects(for).

For arrays: var items = ['one', 'two', 'three', 'four'];

<ul j:each="items :item:i">
  <li>#[i]. #[item]</li>
// or
<j each="items :item:i">
  #[i]. #[item]<br>
// or
[each:items :item:i]
  #[i]. #[item]<br>

i is current index, optional

For objects: var obj = { foo: 'bar' };

<ul j:for="obj :value:key">
  <li>#[key]: #[value]</li>
// or
<j for="obj :value:key">
  #[key]: #[value]<br>
// or
[for:obj :value:key]
  #[key]: #[value]<br>

Jinsoku has shortcut support for if and switch statements.

[if: User.logged_in]              // if (User.logged_in) {
  Welcome back, #[User.username]!
[: User.banned]                   // else if (User.banned) {
  Access denied, you are banned!
[:]                               // else {
  Hello guest, please login!
[/if]                             // }
[case: User.role]                      // switch(User.role) {
  [:'administrator']                   // case 'administrator':
    #[User.username] is administrator.
  [:'moderator']                       // break; case 'moderator':
    #[User.username] is moderator.
  [:]                                  // break; default:
    #[User.username] is user.
[/case]                                // break; }
// simple
// escape html
// set a variable
#[page.title:'Jinsoku Template Engine']
[# var keywords = ['template', 'mvc', 'dom', 'node.js']; #]
[# if (keywords.indexOf('mvc')) { #]
[# } else { #]
[# } #]

We could combine some of the statements:

// main file
<section id="articles" j:each="articles :article" j:include="article"> 
  // the article.html partial will be included here
// article.html file

You can easily extend jinsoku by adding new or replace one of the existing parsers(if, case, each, for, var, evaluate).

To register a parser:

Jinsoku.parser(function(templatenext) {
  // do what you need with template 
  // call next parser 
  next(null, template);

The MIT License

Copyright © 2013 D.G. Shogun147@gmail.com