Skip to content

Instantly share code, notes, and snippets.

Last active August 16, 2016 20:27
Show Gist options
  • Save jescalan/0a963a656a2bbccd2f2ec0b34aac1e3f to your computer and use it in GitHub Desktop.
Save jescalan/0a963a656a2bbccd2f2ec0b34aac1e3f to your computer and use it in GitHub Desktop.
Differences between jade and the spike-standards stack

Spike v0.11.0 Migration Guide

In this release of spike, we switch posthtml for reshape. Reshape is a complete rewrite of posthtml by the static-dev team with several significant differences:

  • It returns a function by default, so that its results can be used as client-side templates
  • It has a much more clear and robust error reporting system, reporting errors that include the line/col in the original source, and exposing a code snippet to show where it came from
  • It has more clearly written documentation, more thorough tests, consistent code style, and 100% coverage on all core modules

However, as a consequence of this move, the jade plugin is no longer available. The jade plugin was always a hack, and had several crippling caveats. In addition, jade does not fit well with reshape's philosophy of breaking functionality down into small modules. As such, we have replaced jade with a small stack of plugins and a custom parser designed to emulate its most important pieces of functionality. This suite of plugins has been in the works for over a month of full time effort. While it is new and certainly there will be bugs discovered, each plugin is thoroughly tested, carefully written, and has held up to initial experimentation. We are calling this bundle of plugins the "spike html standards" plugin pack.

In this document, I want to cover the major differences between jade/pug and reshape/spike-html-standards (SHS) for anyone migrating between the two, or simply as a primer for those moving over in any capacity. It's also important to note that with the SHS route, users can choose to remove any feature they don't want or need, including the sugarml, the whitespace-based parser. This means that everything here can be written with normal html syntax and still still work perfectly fine -- the full syntax of all plugins works with raw html. No plugin depends on another of the plugins. Additionally, extra plugins can add functionality as desired, and it's easy to write and use your own plugin without needing to understand reshape's core architecture, or making a pull request anywhere.

Classes & IDs

Class and ID shortcuts are the same between jade and SHS: wow!


Attributes are almost exactly the same between jade and SHS, except that commas between attributes are not permitted in SHS. If there are commas present, you will get several really bad-looking errors. This is how they should look:

div(class='foo' wow='amaze' @click='callfn')


In jade, an expression would look like this:

// escaped code
p= foo
// unescaped code
p!= foo
// interpolated code
p Hello #{planet}
// code in an attribute
div(id='{{ foo }}-custom')

In SHS, it looks like this:

// escaped code
p {{ foo }}
// unescaped code
p {{{ foo }}}
// interpolated code
p Hello {{ planet }}

Note that in SHS these delimiters are entirely customizable as well if you are not a fan of the handlebars syntax. It is handled by the reshape-expressions plugin


In jade, a conditional would look like this:

if foo === 'bar'
  p foo is bar
else if foo === 'wow'
  p foo is wow
  p foo is not bar or wow

In SHS, like this:

if(condition="foo === 'bar'")
  p foo is bar
elseif(condition="foo === 'wow'")
  p foo is wow
  p foo is not bar or wow

You will see this pattern often in the following examples, SHS's syntax is mostly based on html-valid custom elements rather than parsed language constructs. Because of this, any of its features can be used with straight html, and do not require the whitespace-based parser. This logic is handled by reshape-expressions


In jade, a loop looks like this:

each val in [1,2,3]
  p= val
each val, key in { foo: 'bar' }
  p #{key}: #{val}

In SHS, like this:

each(loop="val, index of [1,2,3]")
  p {{ val }}
each(loop="val, key in { foo: 'bar' }")
  p {{ key }}: {{ val }}

This functionality is handled by reshape-expressions.


In jade, an include would look like this:

include _partial

In SHS, it would look like this:


This logic is handed by reshape-include.


In jade, layouts look like this:

// layout.jade
doctype html
  title Testing
  block content
// index.jade
extends layout
block content
  p hello world!

In SHS, it looks like this:

// layout.sml
doctype html
  title Testing
// index.sml
  p hello world!

Only minor differences here, to make everything compatible with raw html tags. This logic is handled by reshape-layouts.

Content Transforms

In jade, transforming content would look like this:

  This is **markdown**!

In SHS, it looks like this:

p(md) This is **markdown**!

For both jade and SHS, additional configuration is needed to set up the content transform. The way it's handled in SHS is much more robust and clean than jade's. See reshape-content for more details!

Plain Text

In jade, plain text within an element looks like this:

  | Hello there, 
  strong my friend!

In SHS, it is exactly the same. No changes here! This functionality is handled by sugarml, and is not necessary if not using this parser.


SHS does not support mixins. We recommend using a local function to achieve the same effect. If you have a dire need for mixins, you are welcome to write a plugin that supports this functionality!

Unbuffered Code

In jade, unbuffered code looks like this:

- var foo = 'bar'
p= foo

SHS does not support unbuffered code. We recommend using locals instead to handle this use case.


SHS does not support switch/case statements. We recommend using a standard conditional to handle this use case. If you have a dire need for switch statements, you are welcome to pull request them into the expressions plugin and/or write a separate plugin for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment