Jade

everything you didn't know to ask

slides.forbesl.co.uk

Templating Language

  • Produces HTML
  • Supports dynamic code
  • Supports reusability (DRY)

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
  </head>
  <body>
    <h1>Jade - node template engine</h1>
    <article>
      <p>Jade is a terse and simple
         templating language with a
         strong focus on performance
         and powerful features.</p>
    </article>
  </body>
</html>

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade
  <body>
    <h1>Jade - node template engine
    <article>
      <p>Jade is a terse and simple
         templating language with a
         strong focus on performance
         and powerful features.

JADE

doctype html
html(lang="en")
  head
    title Jade
  body
    h1 Jade - node template engine
    article
      p.
        Jade is a terse and simple
        templating language with a
        strong focus on performance
        and powerful features.

Features

Inline HTML

<!--[if IE 8]>
  <html lang="en" class="lt-ie9">
<![endif]-->
<!--[if gt IE 8]><!-->
  <html lang="en">
<!--<![endif]-->

Buffering Output

= jsVariable
!= jsVariable
var html = jade.render(template, {jsVariable: '<foo>'});
&lt;foo&gt;
<foo>

Dynamic Attributes

a(href="mailto:" + email)= email
var html = jade.render(template, {email:'[email protected]'});
<a href="mailto:[email protected]">[email protected]</a>

Interpolatioin

p.
  We would like to welcome #{name} to this talk
  with the following snippet of html: !{html}
var html = jade.render(template, {
  name: 'Forbes',
  html: '<strong>welcome</strong>'
});
<p>We would like to welcome Forbes to this talk
   with the following snippet of html: <strong>welcome</strong></p>

Interpolation Continued

p.
  We would like to welcome #[strong= name] to this talk.
var html = jade.render(template, { name: 'Forbes' });
<p>We would like to welcome <strong>Forbes</strong> to this talk.</p>

Loops and Conditionals

ul
  each value in list
    li= value
if awesome
  p You are awesome
else
  p You could use improvement
var html = jade.render(template, {list: [1, 2], awesome: true});
<ul>
  <li>1</li>
  <li>2</li>
</ul>
<p>You are awesome</p>

Custom JavaScript

(a last resort)

ul
  - list.forEach(function (value) {
    li= value
  - });
var html = jade.render(template, {list: [1, 2]});
<ul>
  <li>1</li>
  <li>2</li>
</ul>

Includes

The simplest way to re-use code

//- root.jade
article
  include ./child.jade
//- child.jade
h2 An Article
p Blah Blah Blah
var html = jade.renderFile('root.jade');
<article>
  <h2>An Article</h2>
  <p>Blah Blah Blah</p>
</article>

Mixins

Re-use blocks with an adjustment

mixin article(name, body)
  article
    h2= name
    p= body
+article('A1', 'The first article')
+article('A2', 'The second article')
<article>
  <h2>A1</h2>
  <p>The first article</p>
</article>
<article>
  <h2>A2</h2>
  <p>The second article</p>
</article>

Mixins with bodies

mixin article(name)
  article
    h2= name
    block
+article('A1')
  p The first article
+article('A2')
  p The second article
<article>
  <h2>A1</h2>
  <p>The first article</p>
</article>
<article>
  <h2>A2</h2>
  <p>The second article</p>
</article>

Inheritance

//- base.jade
doctype html
html
  head
    block title
      title Default Title
  body
    block content
//- article.jade
extends ./base.jade

block title
  title My Article Title

block content
  p An epic article

Inheritance

<!DOCTYPE html>
<html>
  <head>
    <title>My Article Title</title>
  </head>
  <body>
    <p>An epic article</p>
  </body>
</html>

Inheritance - Multi Level

//- authenticated.jade
extends ./base.jade

block title
  title Authenticated
//- anonymous.jade
extends ./base.jade

block title
  title Anonymous

how it works

Compiler Pipeline

Lexer

github.com/jadejs/jade-lexer

  • Reads input one character at a time
  • Uses lots of regular expressions
  • Breaks stream up into tokens
  • Uses character-parser to handle JavaScript

Character-Parser

github.com/ForbesLindesay/character-parser

var parseMax = require('character-parser').parseMax;
var expression = parseMax('foo="(", bar="}") bing bong').src;
assert(expression = 'foo="(", bar="}"');

Parser

github.com/jadejs/jade-parser

  • Reads in a stream of tokens
  • Constructs a tree to represent the abstract logic
  • Loads dependencies via fs.readFileSync
  • Recursively, lexes, parses and links those dependencies
  • Works by compiling the parent template with the current templates blocks in a blocks property

Compiler Pipeline - Revised

Loader

github.com/jadejs/jade-load

  • Loads dependencies via fs.readFileSync
  • Lexes each file using the lexer
  • Parses each file using the parser

Linker

github.com/jadejs/jade-linker

  • Takes an AST with dependency's ASTs nested under the "extends" and "include" nodes.
  • Links them together appropriately

Compiler

github.com/jadejs/jade-code-gen

  • Takes an AST that has already been linked
  • Generates JavaScript code to push HTML strings into a buffer array.
  • Uses jade-runtime, constantinople and with

Jade Runtime

github.com/jadejs/jade-runtime

  • Handles escaping code (replace < with &lt; etc.)
  • Handles joining classes
  • Hanldes stringifying style objects like {color: 'red'} into "color: red"
  • Handles error reporting

In jade@1 this must be loaded as a script tag on any page that uses Jade templates.

In jade@2, the required methods can just be inlined to simplify the build process.

Constantiople

github.com/ForbesLindesay/constantinople

  • Parses JavaScript using acorn
  • Uses acorn-globals to get a list of global variables
  • If that list is empty, evaluate the code to get a constant result

With

github.com/ForbesLindesay/with

  • Parses JavaScript using acorn
  • Uses acorn-globals to get a list of global variables
  • Makes that list of variables available within the template

i.e.

with(obj) {
  buf.push(foo + bar);
}

becomes

var foo = obj.foo,
    bar = obj.bar;
buf.push(foo + bar);

Filters

Example

:markdown
  # Heading 1

  Paragraph of text
<h1>Heading 1</h1>
<p>Paragraph of text</p>

Transformers

github.com/ForbesLindesay/transformers

  • Creates a common API for all string + data -> string transformations
  • Does not include the modules needed for the transformations as a dependency
  • Can only be released as one homgenous mass
var transformer = require('transformers')['transformer-name'];
var ouptut = transformer.render(input, options)

jstransfprmers

github.com/jstransformers

  • Creates a common API for all string + data -> string transformations
  • Include the module needed for the transformation as a dependency
  • Is released as many separate modules
var transformer = require('jstransformer-transformer-name');
var ouptut = transformer.render(input, options)

jstransformers - extreme open governance

github.com/jstransformers

Get involved!!!

Extensibility

React Jade

github.com/jadejs/react-jade

  • Uses jade-lexer
  • Uses jade-parser
  • Uses jade-loader
  • Uses jade-linker
  • Uses jade-runtime
  • Uses constantinople
  • Uses with

React Jade

github.com/jadejs/react-jade

Custom implementation of jade-code-gen

a(href="http://example.com") example
React.createElement('a', {href: 'http://example.com'}, 'example')

React Jade - Browserify

var jade = require('react-jade');
module.exports = jade.compileFile(__dirname + 'input.jade');

becomes

module.exports = function (locals) {
  return React.createElement('a', {
    href: 'http://example.com'
  }, 'example');
}

React Jade - Browserify

var React = require('react');
var jade = require('react-jade');
module.exports = React.createClass({
  onClick: function () { this.setState({clicked: true}) },
  render: jade`
if !this.state.clicked
  button(onClick=this.onClick) Click Me!
else
  p You clicked me!
`
});

React Jade - Browserify

var React = require('react');
module.exports = React.createClass({
  onClick: function () { this.setState({clicked: true}) },
  render: function () {
    if (!this.state.clicked) {
      return React.createElement('button', {
        onClick: this.onClick
      }, 'Click me!');
    } else {
      return React.createElement('p', {}, 'You clicked me!');
    }
  }
});

Support ES6

  • character-parser
  • constantinople
  • with
  • Function constructor

Next Steps

  • Use acorn rather than Function in lexer/parser to check code is a valid expression
  • Add a plugin to support Babel (formally 6to5) before evaluating the template

getting involved

Join the Team - open governance

  • Help add to current documentation
  • Translate the docs to another language
  • Review pull requests
  • Help beginners by answering questions in the issue tracker
  • Write your own fork like react-jade & then-jade
  • Build a jade linter

Forbes Lindesay

slides.forbesl.co.uk

Social Networks

Twitter: @ForbesLindesay

GitHub: @ForbesLindesay

Blog: www.forbeslindesay.co.uk

Open Source

Jade

Browserify Middleware

readable-email.org

brcdn.org

tempjs.org