npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

coffee-templates

v0.0.6

Published

Fastest Minimalist CoffeeScript/JavaScript CoffeeCup/Handlebars/Mustache template engine.

Downloads

5

Readme

Why CoffeeTemplates?

  • 89.5% smaller download than CoffeeCup + Handlebars--but compiles BOTH with just 226 lines.
  • thats just 6.9KB, 4.2KB minified, and 2.1KB gzipped
  • renders between 22% to 80% faster
  • stand-alone client-side in browser or server-side with Node.js with NO dependencies
  • compiling to .js yields one-function-per-template which renders using ONLY string concatenation--the secret to its speed
  • when compiled to .js, no ancillary "engine" library is required to render the template functions. nor is one embedded within the functions.
  • compiling directly from .coffee to .html or aggregated templates.js eliminates a lot of inbetween middleware
  • this means delays, complexities, and potential for double-trees between client and server-side templating engines are eliminated
  • only one language to write; one language to teach/master; one language to rule them all!
  • common functions available to node/js/coffee also available in templates i.e. require() and executed in same scope

Inspired by coffeecup, and ck, and mini-handlebars libraries.

Rendering Coffee to HTML

# this line is only required within node
CoffeeTemplates = require 'coffee-templates'

# initialize new engine
engine = new CoffeeTemplates format: true

# provide template expression
doctype 5
html ->
  head ->
    meta charset: 'utf-8'
    title "#{@title or 'Untitled'} | A completely plausible website"
    meta(name: 'description', content: @description) if @description?

    link rel: 'stylesheet', href: '/css/app.css'

    style '''
      body {font-family: sans-serif}
      header, nav, section, footer {display: block}
    '''

    comment 'Stylus is supported as well'

    stylus '''
      body
        margin: 0
    '''

    script src: '/js/jquery.js'

    coffeescript ->
      $(document).ready ->
        alert 'Alerts suck!'
  body ->
    header ->
      h1 @title or 'Untitled'

      nav ->
        ul ->
          (li -> a href: '/', -> 'Home') unless @path is '/'
          li -> a href: '/chunky', -> 'Bacon!'
          switch @user.role
            when 'owner', 'admin'
              li -> a href: '/admin', -> 'Secret Stuff'
            when 'vip'
              li -> a href: '/vip', -> 'Exclusive Stuff'
            else
              li -> a href: '/commoners', -> 'Just Stuff'

    div '#myid.myclass.anotherclass', style: 'position: fixed', ->
      p 'Divitis kills! Inline styling too.'

    section ->
      # A helper function you built and included.
      breadcrumb separator: '>', clickable: yes

      h2 "Let's count to 10:"
      p i for i in [1..10]

      # Another hypothetical helper.
      form_to @post, ->
        textbox '#title', label: 'Title:'
        textbox '#author', label: 'Author:'
        submit 'Save'

    footer ->
      # CoffeeScript comments. Not visible in the output document.
      comment 'HTML comments.'
      p 'Bye!'


locals =
  title: 'Best website'

# render coffee template to html
console.log engine.render template, locals

Rendering Coffee to Handlebars

engine = new CoffeeTemplates format: true, handlebars: true

template = ->
  ul ->
    for company in @companies
      block "each #{company}", ->
        li '{{this}}'

console.log handlebars_template = engine.render template,
  companies: [ 'google', 'yahoo' ]

Outputs:

<ul>
  {{#each google}}
    <li>{{this}}</li>
  {{/each}}
  {{#each yahoo}}
    <li>{{this}}</li>
  {{/each}}
</ul>

Improving Handlebars/Mustache

Notice that while regular Mustache/Handlebars templates still compile, we took the liberty to engineer several improvements to the compiler:

  • {{#blocks arg...}}{{/blocks}} can also be written as {{blocks arg...}}{{/blocks}}
  • however, blocks are required to take at least one argument
  • blocks are just javascript functions
  • any function that implements function(arg..., cb) { cb(arg...) } can be executed by a block
  • a function need only be in the window/root scope to be used as a helper; no need to define iterators specially
  • blocks can take any number of arguments
  • blocks can also list callback function arguments within parenthesis ()
  • any character that is valid in a function or variable name is a valid block or block argument name
  • therefore, <ul>{{$.each companies}}<li>{{this}}</li>{{/$.each}}</ul> is valid
  • and so is, <ul>{{jQuery.each people, (key, value)}}<li>{{key}}: {{value}}</li>{{/each}}</ul>
engine = new CoffeeTemplates format: true

console.log mustache_template = engine.render ->
  block "each company, (name, data)", ->
    h2 '{{name}}'
    ul ->
      block "each data.people", ->
        li '{{this}}'

Outputs:

{{each company, (name, data)}}
  <h2>{{name}}</h2>
  <ul>
    {{each data.people}}
      <li>{{this}}</li>
    {{/each}}
  </ul>
{{/each}}

Rendering Coffee/Handlebars/Mustache a NoEngine JS Function

mustache_template = '{{each company, (name, data)}}<h2>{{name}}</h2><ul>{{each data.people}}<li>{{this}}</li>{{/each}}</ul>{{/each}}'

console.log template_fn = CoffeeTemplates.compile mustache_template

Outputs:

function anonymous(i) {
var o='',w=function(f,a) {o='';f.apply(i, a);return o};return w(each,[i.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])
}

This single function is a completely stand-alone version of your template, and is all that is needed to render the HTML.

Of course, this could also be used to render XML or some other markup, as well.

Rendering a NoEngine JS Function to HTML

window.each = (o, cb) ->
  for k of o
    cb.apply o[k], [k, o[k]]
  return

console.log html = template_fn company:
  goog: people: ['Larry Page', 'Sergey Brin']
  msft: people: ['Bill Gates']

Outputs:

<h2>goog</h2><ul><li>Larry Page</li><li>Sergey Brin</li></ul><h2>msft</h2><ul><li>Bill Gates</li></ul>

Rendering multiple templates to function

console.log templates = CoffeeTemplates.compileAll
  'views/users/index': (CoffeeTemplates.compile mustache_template, false)
  'views/users/index_by_company': (CoffeeTemplates.compile mustache_template, false)

Outputs:

function anonymous(n,i) {
var o='',w=function(f,a) {o='';f.apply(i, a);return o},t={}
t["views/users/index"]=function(){return w(each,[this.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])}
t["views/users/index_by_company"]=function(){return w(each,[this.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])}
return t[n].call(i)
}

From here you would normally save the function in a file like static/public/assets/templates.js.

Further examples

As usual, for the latest examples, review the easy-to-follow ./test/test.coffee.

Or try it immediately in your browser with codepen.

Useful Tools